I have defined a method in a trait:
trait myTrait {
def myMethod(entity: Entity = null, name: String)
}
val t = ....//concrete implementation of myTrait
t.myMethod("John")
I have provided default value of null for entity in method definition.
This give compilation error as: Type mismatch, required MyCustomClass found String
Why is it so?
Use the name of your argument.
t.myMethod(name = "John")
It also works for case class when you want to .copy
case class MyStruct(a: String, b: String)
val a = MyStruct("foo", "bar")
val b = a.copy(b = "clazz")
See https://docs.scala-lang.org/tour/named-arguments.html
Also, in scala, the presence / absence of value is usually represented by Option[T] like
def myMethod(entity: Option[Entity] = None, ... )
Related
Given a type which has a "converter", I would like to have automatic conversion on method call using this type's companion object. That is, given the following definition,
case class Converted(name: String)
trait Converter[A] {
def perform: Converted
}
implicit val StringConverter = new Converter[String] {
def perform = Converted("String")
}
make the following code to work:
implicit def toConverter(a: String.type): Converted =
implicitly[Converter[String]].perform // Error: `Found String.type, required AnyRef`
def f(needsConverted: Converted) = ???
f(String) // <- That's what I would like to be able to write.
But this fails and both attempts for conversion fail. Note that I cannot change f because it is provided by a third-party library and there are many of them.
Can I make f(String) compile using implicits?
If not possible for Strings, what about classes which do have a companion object, can I do this generically like:
object TheClass
case class TheClass()
implicit val TheClassConverter = new Converter[TheClass] {
def perform = Converted("TheClass")
}
implicit def toConverter[A: Converter](a: A.type): Converted =
implicitly[Converter[A]].perform // Error: `Not found value A`
implicit def toConverter(a: TheClass.type): Converted =
implicitly[Converter[TheClass]].perform // This works but is not generic
f(TheClass) // This works.
Can I make the first toConverter to compile ?
Instead of defining an implicit instance for the type MyClass you can define an implicit instance for the companion type MyClass.type.
implicit val TheClassConverter: Converter[MyClass.type] = new Converted[MyClass.type] {
def perform = Converted("MyClass")
}
Can I make f(String) compile using implicits?
No. You can define a value called String, of course, but it won't be related to the type String.
implicit toConverter[A: Converter](a: A.type): Converted =
implicitly[Converter[A]].perform
A in A.type must be a value; it is not related to the type parameter A.
In fact, so far as Scala's type system is concerned, there is no relationship between a class/trait and its companion object. So you can't do what you want generically.
Of course, if you don't insist on using () instead of [], it becomes trivial:
def f1[A: Converter] = f(implicitly[Converter[A]].perform)
f1[String]
f1[TheClass]
Not sure, what are you trying to accomplish, but following works for me
case class Converted(name: String)
trait Converter[A] {
def perform: Converted
}
implicit def toConverted(name: String) = Converted("String")
implicit def toIntConverted(int: Int) = Converted("Int")
def f(needsConverted: Converted): String = needsConverted.name
f("some")
f(5)
I am trying to support an abstraction of an ID type for a framework. Example here:
object AmINecessary {
case class StringConverter[T](op: String => T)
implicit val toInt = new StringConverter[Int](_.toInt)
implicit val toLong = new StringConverter[Long](_.toLong)
}
class Foo[ID] {
// ID can be String, Long, or Int
import AmINecessary._
// If ID is string, return string, otherwise convert to ID
def getID(id: String)(implicit c: StringConverter[ID] = null): ID = if (c == null) id.asInstanceOf[ID] else c.op(id)
}
This is then used as:
val fooString = new Foo[String]
val fooLong = new Foo[Long]
val fooInt = new Foo[Int]
fooString.getID("asdf") // "asdf":String
fooLong.getID("1234") // 1234:Long
fooInt.getID("1234") // 1234:Int
fooInt.getID("asdf") // java.lang.NumberFormatException
This works as expected. My questions are:
using an optional implicit by defaulting it to null then branching on it feels bad. What is the scala way to accomplish that?
Is it really necessary to write implicit conversions for a string to long or int?
I think the best option would be to simply add an implicit StringConverter[String] and remove the default null value.
That way your fooString works without risking a ClassCastException for every other type.
object AmINecessary {
case class StringConverter[T](op: String => T)
implicit val toInt = new StringConverter[Int](_.toInt)
implicit val toLong = new StringConverter[Long](_.toLong)
implicit val idConverter = new StringConverter[String](identity)
}
class Foo[ID] {
import AmINecessary.StringConverter
def getID(id: String)(implicit c: StringConverter[ID]): ID = c.op(id)
}
Regarding your question 2, the type class approach is not really necessary (but note that there are no implicit conversions here). You can also do it like this:
abstract class Foo[ID] {
def getID(id: String): ID
}
class FooInt extends Foo[Int] {
def getID(id: String) = id.toInt
}
class FooLong extends Foo[Long] {
def getID(id: String) = id.toLong
}
class FooString extends Foo[String] {
def getID(id: String) = id
}
1) About implicit defaulting to null, you could just:
object Unsafe {
implicit val toT[T] = new StringConverter[T](_.asInstanceOf[T])
}
2) It doesn't seem as a good idea. First, because you're hiding asInstanceOf, which is unsafe operation (potential runtime exception). Second, the more explicit conversion is - the better.
If you expect some complex transformations - it's better to return option from your getID method:
def getId[T](id: String)(converter: Option[StringConverter] = None) = converter.map(_.op(id))
However, default parameters aren't the best approach either. I'd stick with compile-time error requiring user to either write own converter or explicitly do an asInstanceOf, in general.
In your concrete case asInstanceOf doesn't make much sense as the only type it would work for is String, like getId[String], so what's the point of calling getId then?
i have following class hierarchy.
trait Item {val id: String}
case class MItem(override val id: String, val name: String) extends Item
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name)
val d = new DItem("1", "one", "another one")
println(d)
Expected Output
DItem(1, one, another one)
Actual Output
Mitem(1,one)
Why is this happening. What is recommended so that i get the real type of my object and the not type of super class.
This is not a type erasure. You are getting this result because DItem gets toString() implementation inherited from Mitem. You have to override it to get what you want
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
override def toString = s"DItem($id, $name, $name2)"
}
So here is a result:
scala> val d = new DItem("1", "one", "another one")
d: DItem = DItem(1, one, another one)
scala> println(d)
DItem(1, one, another one)
It is almost always a bad idea to inherit from case classes because besides toString successor class will also inherit equals and hashCode.
Another drawback is limited pattern-matching for such successor classes i.e it is impossible to use such classes in case branches and may lead to confusing errors.
Example
case class A(id: String)
class B(id: String, name: String) extends A(id)
new B("foo", "bar") match {
case A(id) => println(id)
case other => println(other)
}
You may expect that there is no error in this code, but you'll get
<console>:17: error: constructor cannot be instantiated to expected type;
found : A
required: B
case A(id) => println(id)
^
However if you'll infer a type for B instance explicitly it will work
scala> new B("foo", "bar").asInstanceOf[A] match {
| case A(id) => println(id)
| case other => println(other)
| }
foo
So... Inheriting from case classes is very error-prone and confusing and should be avoided unless you know what are you doing.
Inheriting from case classes is deprecated as far as I know. So case classes can (should) only inherit from regular classes.
doing println usually invoke toString on the object pass on it.
so what happen on your code is, it will invoke the toString implementation of the object, it happens to be MItem that has this implementation.
so you need to override the toString on DItem like this:
class DItem(override val id: String, override val name: String, val name2: String) extends MItem(id, name) {
override def toString = s"DItem($id, $name, $name2)"
}
if you want to just get the type of the object you can use getClass.
println(d.getClass)
I would like to limit type [R] with only specific tuples which are listed somewhere. My main goal is: to ensure that compiler raises an error if any other tuple rather than one of expected is passed to the following trait.
trait QV_Storable[R <: QV_Cue] {
val prefix: String
val tableName = prefix.concat(Random.alphanumeric take 8 mkString)
def Make(rows: Iterable[R.storageColumns]): String
}
Then I do expect QV_Storable accept only tuples listed in SegmentCue because it is subtupe of QV_Cue. Pay attention that I pass R.storageColumns to Make(rows: Iterable[R.storageColumns]). Thus, I do expect a way to access parameters of class type.
trait QV_Cue{
type storageColumns <: Product
type metaColumns <: Product
}
object SegmentCue extends QV_Cue {
type storageCols = (CoreDataID, String)
type metaCols = (Int, String, Date, Int)
}
Is there anyway to limit it this way?
It would not be great to subtype from Tuple1, Tuple3, etc.
You can provide a type class, which only sufficient tuples implement:
trait Ok[T] {}
// (Int, Int) is Ok
implicit val tupleIntIntOk: Ok[(Int, Int)] = new Ok[(Int, Int)] {}
// (String, _) is Ok too
implicit def tupleStringAOk[A]: Ok[(String, A)] = new Ok[(String, A)] {}
trait QV_Storable[R] {
def method(implicit rok: Ok[R]): Unit
// ...
}
In this scenario you can't call QV_Storable.method if there aren't rok value of the right type in the scope.
This way you can create QV_Storable with any R, but can't actually use any without proper Ok.
A bit different approach is to make similar trick on type-level. Yet there aren't implicit resolution:
sealed trait Ok[T] {}
case class TupleIntIntOk() extends Ok[(Int, Int)]
case class TupleStringAOk[A]() extends Ok[(String, A)]
trait QV_Storable[R, OkR <: Ok[R]] {
val prefix: String
// ...
}
// compiles
val foo = new QV_Storable[(Int, Int), TupleIntIntOk] { val prefix = "xy" }
// doesn't
val bar = new QV_Storable[(Int, String), /* nothing to put here */] { val prefix = "bar "}
Section 4.6.2 of the Scala Language Specification Version 2.8 describes repeated parameters and says:
The last value parameter of a parameter section may be suffixed by “*”, e.g. (..., x:T*). The type of such a repeated parameter inside the method is then the sequence type scala.Seq[T].
However, this code:
abstract class A { def aSeq : Seq[A] }
class B(val aSeq : A*) extends A
class C extends B { override val aSeq :Seq[A] = Seq() }
give an error when compiled:
overriding value aSeq in class B of type A*; value aSeq has incompatible type
The compiler seems to indicate that A* is a distinct type from Seq[A].
Investigating the actual class of aSeq in this case shows it to be an instance of scala.collection.mutable.WrappedArray$ofRef but even the following code fails to compile with the same message:
class C extends B { override val aSeq = new ofRef(Array[A]()) }
So the question is, how do I go about overriding a member defined by a repeated parameter on the class?
In case you're wondering where this is coming from, that is exacly what scala.xml.Elem does to override the child method in scala.xml.Node.
Your issue can be summarized as:
scala> class A { def aSeq(i: Int*) = 1 }
defined class A
scala> class B extends A { override def aSeq(i: Seq[Int]) = 2 }
<console>:6: error: method aSeq overrides nothing
class B extends A { override def aSeq(i: Seq[Int]) = 2 }
The methods have different types. The spec says (emphasis mine):
The type of such a repeated parameter inside the method is then the sequence type scala.Seq[T]
As Int* and Seq[Int] aren't inside the method, this particular sentence does not apply.
Interestingly, this following code shows that the methods have different types before erasure but the same after:
scala> class G { def aSeq(i:Int*) = 1; def aSeq(i:Seq[Int]) = 2 }
<console>:5: error: double definition:
method aSeq:(i: Seq[Int])Int and
method aSeq:(i: Int*)Int at line 5
have same type after erasure: (i: Seq)Int
class G { def aSeq(i:Int*) = 1; def aSeq(i:Seq[Int]) = 2 }
So the question then becomes, why your B class can extend your A abstract class. There may be an inconsistency in the spec there. I don't know...
Edit: I re-read the spec and I can't figure out if there is anything related to repeated parameters and overriding. There does not seem to be anything about return type of repeated parameters, which is what you get for the val aSeq accessor method.
I think Mark's answer is a perfectly valid approach. In case you can't follow it, you can use the following workaround:
class C extends B {
private def aSeqHelper(a: A*) = a
override val aSeq = aSeqHelper(Seq[A](): _*)
}
So for instance:
import scala.xml._
class ElemX extends Elem("pref", "label", <xml a="b"/>.attributes, TopScope) {
private def childHelper(c: Node*) = c
override val child = childHelper(<foo/><bar/>: _*) }
Then:
scala> new ElemX
res4: ElemX = <pref:label a="b"><foo></foo><bar></bar></pref:label>
The copy method of xml.Elem uses it like this
def copy(
prefix: String = this.prefix,
label: String = this.label,
attributes: MetaData = this.attributes,
scope: NamespaceBinding = this.scope,
child: Seq[Node] = this.child.toSeq
): Elem = Elem(prefix, label, attributes, scope, child: _*)
So you can override the value in the B constructor
class C extends B(aSeq = Seq(): _*)
Or declare it as a parameter of the class C
class C(seq: Seq[A]) extends B(aSeq = seq: _*)
Though I am not sure it answers your question!
The spec never allowed for repeated types to leak out in this way. The compiler was changed in July 2011 to enforce this.
For more background, check out the comments in the ticket:
https://issues.scala-lang.org/browse/SI-4176