2015-01-05 27 views
7

Sto cercando di capire quello che sembra strano comportamento quando si tratta di null e digitare annotazioni all'interno di una comprensione.scala eccezione in per-comprensione con annotazione tipo

Per fare un esempio:

def f(): String = null 

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

risultati nella attesi:

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

tuttavia, se posso aggiungere un tipo di annotazione al tipo di b

def f(): String = null 

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

allora ottengo un'eccezione di runtime:

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

Perché ciò accade? Ilnon è implicito nel tipo String nel primo esempio? Che cosa cambia l'annotazione del tipo esplicito nel secondo esempio?

(Nota, esempi sono stati eseguiti in Scala 2.11.4)

+0

Il secondo esempio viene compilato in un match, come si può vedere usando 'reify' nel repl:' import scala.reflect.runtime.universe ._; reificare {per {...}} '. Non posso dirti perché, però. AIUI 'null' non corrisponde perché la corrispondenza funziona sul tipo (o valore) di runtime, anche se' b' ha un tipo in fase di compilazione 'String'; questo è in un certo senso un buco nel sistema dei tipi, e il codice scala dovrebbe generalmente evitare di usare valori nulli. – lmm

+0

Non sono un fan dei null, credimi! Nel codice effettivo in cui mi sono imbattuto in questo stavo usando 'Try's per l'integrazione con qualche codice java legacy. Tuttavia, qui mi sembra ininfluente: l'aspetto che trovo bizzarro (pauroso?) È che l'aggiunta di più specificità di tipo risulta in un'eccezione di runtime (non intuitiva). –

+0

È una stranezza del linguaggio che mettere i tipi in un 'for' /' yield' produce una corrispondenza di pattern; non è un problema che si verificherebbe con tipi "ordinari" sulle espressioni. Tutto quello che posso dire è che è sfortunato, ma probabilmente non può essere cambiato in questa fase; Tendo ad evitare di dare mai un tipo sul lato sinistro di un for/yield per questo motivo:/ – lmm

risposta

7

null non è un esempio di tutto ciò:

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

Tipo pattern specifica non nullo.

Un trucco per mostrare la traduzione è di commentare spettacolo:

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

Questo mostra cosa sta andando storto, ma è probabilmente un bug perché non c'è motivo di associare il pattern su quest'ultima mappa dato che _by construction_ ha già il tipo corretto (in 'val b: String = f'). (È anche discutibilmente un bug che la funzione di identità non è stata ottimizzata, ma possiamo immaginare un caso più complesso.) –

+0

@RexKerr sì, probabilmente. È un argomento più forte se usava la vecchia sintassi 'val b: String = f()', forse modulo SI-900 dove type pat on generator significa filter. Ma non ottimizzerai la partita ordinaria. Forse dovremmo iniziare a scrivere "val i = 42: Int". Quindi 'val s: String = null: String' potrebbe fallire come se fosse racchiuso in' Tuple1'. –

+0

La specifica del linguaggio impone che lo smascheramento dell'annotazione del tipo nella comprensione sia implementato come un pattern match? –