2014-11-05 14 views
6

Ho il seguente ciclo per rendimento che prende in un valore booleano e deve sia produrre alcuni (stringa) o None, a seconda della booleana:Può un rendimento di resa in Scala Scala se si passa a un'opzione?

val theBoolean = false 

val x: Option[String] = 
for { 
    theArg <- theBoolean 
} yield { 
    if (theArg) { 
    "abc" 
    } else { 
    None 
    } 
} 

Questa grande opera se theBoolean è in realtà un valore booleano come false. Tuttavia se volevo passare in un Option[Boolean]:

val theBoolean = Some(false) 

sembra Scala applica automaticamente un wrapper Alcuni() per il ritorno Nessuno - ottengo una denuncia che "Espressione di Tipo di opzione [Serializable] non è conforme al tipo previsto Opzione [stringa] "(con Nessuno che è serializzabile). La resa è perfettamente felice con la stessa stringa di ritorno anche se (non diventa un'opzione [Opzione [String]]

Come potrei restituire un Nessuno in questo caso?

+1

ho scoperto che in realtà None.get rende il compilatore felice. Ma sembra strano usarlo in questo modo. – Nathan

+0

Che cosa sta cercando di ottenere questo codice? –

+0

Viene passata un'opzione - in determinate condizioni voglio estrarne una stringa, ma in altre condizioni voglio che sia None. – Nathan

risposta

13

A per-la comprensione è solo zucchero sintattico per una serie di flatMap, map e filter. Diamo desugar il codice allora:

val theBoolean = Some(false) 

val x = theBoolean.map { theArg => 
    if (theArg) { 
    "abc" 
    } else { 
    None 
    } 
} 

Come si può vedere, si sta solo la mappatura sul valore del Option, quindi si tornerà neanche Some(abc), Some(None) o None (nel caso in cui sia già theBooleanNone) .

La disponibilità tipo comune di None e "abc" è java.Serializable, ecco perché il tipo di x viene dedotto come Option[Serializable], che come insignificante come Option[Any].

soluzioni possibili sono:

  • utilizzando un flatMap

    theBoolean.flatMap(theArg => if (theArg) Some("abc") else None) 
    

    o anche più breve di filtraggio

    theBoolean.flatMap(if (_) Some("abc") else None) 
    
  • e mappatura

    theBoolean.withFilter(identity).map(_ => "abc") 
    

Dove ho utilizzato identity poiché si sta verificando il valore stesso.

Chiaramente si può sempre sfruttare la zucchero sintattico fornita da un per-comprensione, anche se in realtà non fare la differenza in questo caso

for { 
    theArg <- theBoolean 
    if theArg 
} yield "abc" 
+0

Grazie, la spiegazione è stata super utile! – Nathan

1

Hai ragione, tutto all'interno del Il blocco yield è racchiuso in un'opzione (è così che funzionano le incomprensioni per le opzioni). In generale, le incomprensioni descrivono cosa dovrebbe essere fatto con il contenuto che può essere trovato all'interno della monade su cui viene invocata la comprensione, ma la fine risultato (per il mondo esterno al blocco yield) è ancora una monade dello stesso tipo (ad esempio Opzione, Prova o Elenco)

Su ancora più generico: ci sono molte descrizioni su cosa sia una monade. Puoi presumere che una monade sia quella infame scatola di Schrödinger e stai riflettendo su cosa potrebbe accadere con quel gatto nascosto lì, ma tutto ciò rimane ancora una possibilità perché la scatola non è ancora aperta.

Un modo possibile per fare ciò che si vuole:

val theBoolean = false 

val x: Option[String] = 
for { 
    theArg <- theBoolean if theArg 
} yield { 
    "abc" 
} 
0

Invece di una di comprensione suona come si desidera una flatMap invece di una per la comprensione.

scala> Some(false).flatMap(if (_) Some("abc") else None) 
res4: Option[String] = None