Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

scala exception in for-comprehension with type annotation

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)

like image 990
Brian Kent Avatar asked Jan 05 '15 23:01

Brian Kent


1 Answers

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
like image 102
som-snytt Avatar answered Jan 02 '23 06:01

som-snytt