Search code examples
scalafor-comprehensionscala-2.11

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)


Solution

  • 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