I am trying to understand what seems like strange behavior when dealing with nulls and type annotations inside a for-comprehension.
As an example:
def f(): String = null
for {
a <- Option("hello")
b = f()
} yield (a, b)
results in the expected:
//> res0: Option[(String, String)] = Some((hello,null))
however, if I add a type annotation to the type of b
def f(): String = null
for {
a <- Option("hello")
b: String = f()
} yield (a, b)
then I get a runtime exception:
//> scala.MatchError: (hello,null) (of class scala.Tuple2)
Why does this happen? Isn't b
implicitly of type String
in the first example anyway? What does the explicit type annotation in the second example change?
(Note, examples were run in Scala 2.11.4)
null
is not an instance of anything:
scala> (null: String) match { case _: String => }
scala.MatchError: null
... 33 elided
scala> val s: String = null
s: String = null
scala> s.isInstanceOf[String]
res1: Boolean = false
http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#type-patterns
Type pattern specifies non-null.
One trick for showing the translation is to comment show:
scala> for {
| a <- Option("hello")
| b: String = f()
| } yield (a, b) // show
object $read extends scala.AnyRef {
def <init>() = {
super.<init>;
()
};
object $iw extends scala.AnyRef {
def <init>() = {
super.<init>;
()
};
import $line4.$read.$iw.$iw.f;
object $iw extends scala.AnyRef {
def <init>() = {
super.<init>;
()
};
val res1 = Option("hello").map(((a) => {
val b: String = f;
scala.Tuple2(a, b)
})).map(((x$1) => x$1: @scala.unchecked match {
case scala.Tuple2((a @ _), (b @ (_: String))) => scala.Tuple2(a, b)
}))
}
}
}
scala.MatchError: (hello,null) (of class scala.Tuple2)
at $anonfun$2.apply(<console>:10)
at $anonfun$2.apply(<console>:10)
at scala.Option.map(Option.scala:145)
... 39 elided
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With