Option getOrElse type mismatch error - scala

Why does this code raise a type mismatch error in Scala 2.9.2? I expected that getOrElse returns type String but actually it returns java.io.Serializable:
scala> implicit def StringToOption(s:String) = Option(s)
StringToOption: (s: String)Option[String]
scala> "a".getOrElse("")
res0: String = a
scala> var opt:Option[String] = "a".getOrElse("")
<console>:8: error: type mismatch;
found : java.io.Serializable
required: Option[String]
var opt:Option[String] = "a".getOrElse("")
^
This is OK:
scala> implicit def StringToOption(s:String): Option[String] = Option(s)
StringToOption: (s: String)Option[String]
scala> var b:Option[String] = "a".getOrElse("") toString
b: Option[String] = Some(a)

It's an unwanted case of incomplete tree traversal. The signature of getOrElse allows type widening, so when it realizes that String is not Option[String] it first tries to fill in a different type ascription on getOrElse, i.e. Serializable. But now it has "a".getOrElse[Serializable]("") and it's stuck--it doesn't realize, I guess, that the problem was making the type too general before checking for implicits.
Once you realize the problem, there's a fix:
"a".getOrElse[String]("")
Now the typer doesn't wander down the let's-widen path, and finds the implicit.

Related

Type error when chaining map with toSet and using function literal with underscore [duplicate]

Can somebody explain why the following does not work. Somehow looses the compile some information for the type inference when i do toSet, but i don't understand why.
scala> case class Foo(id: Int, name: String)
defined class Foo
scala> val ids = List(1,2,3)
ids: List[Int] = List(1, 2, 3)
scala> ids.toSet.map(Foo(_, "bar"))
<console>:11: error: missing parameter type for expanded function ((x$1) => Foo(x$1, "bar"))
ids.toSet.map(Foo(_, "bar"))
^
scala> ids.map(Foo(_, "bar")).toSet
res1: scala.collection.immutable.Set[Foo] = Set(Foo(1,bar), Foo(2,bar), Foo(3,bar))
Suppose I've got the following:
trait Pet {
def name: String
}
case class Dog(name: String) extends Pet
val someDogs: List[Dog] = List(Dog("Fido"), Dog("Rover"), Dog("Sam"))
Set isn't covariant in its type parameter, but List is. This means if I have a List[Dog] I also have a List[Pet], but a Set[Dog] is not a Set[Pet]. For the sake of convenience, Scala allows you to upcast during a conversion from a List (or other collection types) to a Set by providing an explicit type parameter on toSet. When you write val a = ids.toSet; a.map(...), this type parameter is inferred and you're fine. When you write ids.toSet.map(...), on the other hand, it's not inferred, and you're out of luck.
This allows the following to work:
scala> val twoPetSet: Set[Pet] = someDogs.toSet.take(2)
twoPetSet: Set[Pet] = Set(Dog(Fido), Dog(Rover))
While this doesn't:
scala> val allDogSet: Set[Dog] = someDogs.toSet
allDogSet: Set[Dog] = Set(Dog(Fido), Dog(Rover), Dog(Sam))
scala> val twoPetSet: Set[Pet] = allDogSet.take(2)
<console>:14: error: type mismatch;
found : scala.collection.immutable.Set[Dog]
required: Set[Pet]
Note: Dog <: Pet, but trait Set is invariant in type A.
You may wish to investigate a wildcard type such as `_ <: Pet`. (SLS 3.2.10)
val twoPetSet: Set[Pet] = allDogSet.take(2)
^
Is this worth the confusion? I don't know. But it kind of makes sense, and it's the decision the Collections API designers made for toSet, so we're stuck with it.

REPL could not find implicit

Hi I have an implicit method like:
implicit def strToOpt(str: String): Option[String] = Option(str)
and it works for simple conversion, but when I type
implicitly[String]
I get
error: could not find implicit value for parameter e: String
Does REPL have some limitations in term of finding implicits?
I think you have misunderstood implicitly. It is defined as:
def implicitly[T](implicit e: T): T = e
Which roughly means, that implicitly[T] will return you an object of type T which is available in current scope (scope is not the precise word. Compiler looks at many places)
In your case doing implicitly[String] simply means that some object of type String is available.
For example this is valid:
scala> implicit val x = "Hey"
x: String = Hey
scala> implicitly[String]
res12: String = Hey
But what you rather need to do is:
scala> implicitly[(String) => Option[String]]
res10: String => Option[String] = <function1>
scala> res10("asd")
res11: Option[String] = Some(456)
PS: Note answer by #Ende Neu works as:
implicitly[Option[String]]("123")
Doing implicitly[Option[String]]("123"), in the implicitly function it takes T as argument which is String. But you have manually provided Option[String] as type parameter of method. So the compiler searches for a function again (by using implicitly again) that does String => Option[String] which it finds in your function.

Scala isInstanceOf and type erasure

I'm confused as how isInstanceOf works in Scala. If I do something like this:
val x: Int = 5
x.isInstanceOf[Int]
Given that Scala does type erasure, shouldn't the JVM remove all type information during runtime?
It's not all type information, just information about generic types. Consider this:
scala> val l = List("foo")
l: List[String] = List(foo)
scala> l.isInstanceOf[List[String]]
res0: Boolean = true
scala> l.isInstanceOf[List[Int]]
<console>:9: warning: fruitless type test: a value of type List[String] cannot also be a List[Int] (the underlying of List[Int]) (but still might match its erasure)
l.isInstanceOf[List[Int]]
^
res1: Boolean = true
They both return true, because the erased type is List.

Why does the explicit syntax for creating Tuples only allow AnyRefs as type annotations?

This code works:
scala> val x = ""
x: java.lang.String = ""
scala> Tuple2[x.type, x.type](x,x)
res5: (x.type, x.type) = ("","")
This one doesn't:
scala> val y = 0
y: Int = 0
scala> Tuple2[y.type, y.type](y,y)
<console>:9: error: type mismatch;
found : y.type (with underlying type Int)
required: AnyRef
Note: an implicit exists from scala.Int => java.lang.Integer, but
methods inherited from Object are rendered ambiguous. This is to avoid
a blanket implicit which would convert any scala.Int to any AnyRef.
You may wish to use a type ascription: `x: java.lang.Integer`.
Tuple2[y.type, y.type](y,y)
^
As well as this one:
scala> val z = ()
z: Unit = ()
scala> Tuple2[z.type, z.type](z,z)
<console>:9: error: type mismatch;
found : z.type (with underlying type Unit)
required: AnyRef
Note: Unit is not implicitly converted to AnyRef. You can safely
pattern match `x: AnyRef` or cast `x.asInstanceOf[AnyRef]` to do so.
Tuple2[z.type, z.type](z,z)
^
The language specification says
A singleton type is of the form p.type, where p is a path pointing to
a value expected to conform (ยง6.1) to scala.AnyRef.
What's the rationale behind that and would it make sense to lift that restriction like it recently happened with 0.getClass?
As you can see on Scala class hierarchy Int, Unit, Boolean (and others) are not subclasses of AnyRef, because they are translated into java int, void, boolean and so on, witch are not subclasses of Object.

How do you invoke a Function1[_, String] in Scala?

I answered a question about a map of functions in Defining a Map from String to Function in Scala which led to a Function1[_, String] which I believe is correct as far as the typing question but possibly useless because I don't know how to invoke such a function:
scala> def f(x: Int) = x.toString
f: (x: Int)java.lang.String
scala> f(8)
res0: java.lang.String = 8
scala> val g: Function1[_, String] = f _
g: Function1[_, String] = <function1>
scala> g(8)
<console>:8: error: type mismatch;
found : Int(8)
required: _$1 where type _$1
g(8)
^
scala> val h: Function1[Int, String] = g
<console>:7: error: type mismatch;
found : (_$1) => String where type _$1
required: (Int) => String
val h: Function1[Int, String] = g
Is there any way to use g?
scala> g.asInstanceOf[Any => String](5)
res3: String = 5
It will work because all functions erase to the same thing: Function1[AnyRef, AnyRef]. When you specify it as Any, then passing an AnyVal will auto-box it on call (and it will be auto-unboxed at the method).
However, you do have to pass the correct parameter type. Or else...
scala> g.asInstanceOf[Any => String](true)
java.lang.ClassCastException: java.lang.Boolean cannot be cast to java.lang.Integer
I would say that it's like if you've cast an object of type String as Any, if you want to use a method defined in String you have to cast it back as a String.
You cast the function as a function that takes an argument of an existential type (which is what _ in a type context means), so you can't use it as a function that takes an Int. To use it as a function that takes an Int you have to cast it back.
The same problem exists when pattern matching with collections, or other generic classes:
def firstLength(collection: Any): Int ={
collection match {
// this is how you would have liked to write it
// case list: List[String] => list.head.length
// case map: Map[String, String] => map.values.head.length
// but this is how you have to write it because of type erasure
case list: List[_] => list.asInstanceOf[List[String]].head.length
case map: Map[_, _] => map.asInstanceOf[Map[String, String]].values.head.length
}
}
The type information isn't there, so you can't match on List[String], instead you have to match on the existential type List[_] (might be wrong on how you say that, it's not the generic type that is existential, I think) and then cast. This is more or less exactly the problem you have, the type you're after has been erased, and there's no way to get it back (unless you can use the same trick with ClassManifest that can be used to get around type erasure in cases like the one above [but not actually the case above, because it's a bit sloppy]).