2015-01-05 12 views
7

for-comprehensionの中でnullsと型の注釈を扱うときに、奇妙な動作のように思えるものを理解しようとしています。一例として、型の注釈での理解のためのスカラーの例外

:期待で

def f(): String = null 

for { 
    a <- Option("hello") 
    b = f() 
} yield (a, b) 

結果:

//> res0: Option[(String, String)] = Some((hello,null)) 

しかし、私はb

def f(): String = null 

for { 
    a <- Option("hello") 
    b: String = f() 
} yield (a, b) 

の型に型注釈を追加した場合、私は取得実行時例外:

//> scala.MatchError: (hello,null) (of class scala.Tuple2) 

どうしてですか?最初の例でb暗黙のうちにタイプStringではありませんか? 2番目の例の明示的な型アノテーションは何を変更しますか?

(注意、例はScalaの2.11.4で行った)

+0

あなたはREPLで 'reify'使用して見ることができるように、第2の例では、試合にコンパイル:'インポートscala.reflect.runtime.universeを._; reify {for {...}} '。私はなぜあなたに言ってもらえません。 AIUIの 'null'は、' b'が 'String'のコンパイル時の型を持っていても、マッチングが実行時の型(または値)に作用するので一致しません。これはある意味では型システムの穴であり、スカラーコードは一般的にヌルの使用を避けるべきです。 – lmm

+0

私はnullsのファンではない、私を信じて!私がこれを見つけた実際のコードでは、私はいくつかのレガシーJavaコードと統合するために 'Try'sを使っていました。しかし、nullは私にとっては偶然であるように思えます。私が気づいているのは、タイプ特異性を増やすと(直感的ではない)実行時例外が発生するということです。 –

+0

'for' /' yield'に型を入れることは、パターンマッチをもたらすという奇妙なことです。式の "普通の"型では問題はありません。私が言うことは残念ですが、おそらくこの段階で変更することはできません。私はこの理由でfor/yieldの左側にタイプを与えることを避ける傾向があります:/ – lmm

答えて

7

nullは何のインスタンスではありません。

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

タイプパターンがnull以外を指定します。

翻訳を示すための1つのトリックはショーをコメントすることです:

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 
+0

それは何が起こっているのかを示していますが、おそらくバグでしょう。なぜなら、construction_は正しいタイプ(val b:String = f')を持っているので、最後のマップでパターンマッチングする理由がないからです。 (間違いなく、アイデンティティ関数が最適化されていないバグかもしれませんが、より複雑なケースを想像することができます) –

+0

@RexKerrはい、間違いなく。古い 'val b:String = f()'構文を使用した場合、より強力なargです。generatorでtype patがフィルタを意味する場合、SI-900を法とする可能性があります。しかし、あなたは通常のマッチを最適化しません。たぶん、私は 'val i = 42:Int'と書くべきでしょう。次に、 'val s:String = null:String'は' Tuple1'でラップされたように失敗する可能性があります。 –

+0

言語仕様は、理解の型注釈の脱糖がパターンマッチとして実装されることを指示していますか? –

関連する問題