2013-08-10 5 views
8

voglio eseguire una logica se è impostato il valore di un'opzione.se (Option.nonEmpty) vs Option.foreach

Provenendo da un ambiente java, ho usato:

if (opt.nonEmpty) { 
    //something 
} 

Andando un po 'più in scala, posso scrivere che come:

opt.foreach(o => { 
    //something 
}) 

Qual è la migliore? Il "foreach" sembra più "idiomatico" e meno Java, ma è meno leggibile - "foreach" applicato a un singolo valore suona strano.

risposta

16

Il tuo esempio non è completo e non si utilizza la sintassi minima.Basta confrontare queste due versioni:

if (opt.nonEmpty) { 
    val o = opt.get 
    // ... 
} 

// vs 

opt foreach { 
    o => // ... 
} 

e

if (opt.nonEmpty) 
    doSomething(opt.get) 

// vs 

opt foreach doSomething 

In entrambe le versioni ci sia in testa più sintattica nella soluzione if, ma sono d'accordo che foreach su un Option può essere fonte di confusione se si pensa di esso solo come valore opzionale.

Invece foreach descrive che si desidera fare una sorta di effetti collaterali, che fa un sacco di senso se si pensa di Option essere una monade e foreach solo un metodo per trasformarla. L'utilizzo di foreach ha inoltre il grande vantaggio di semplificare i processi di refactoring: basta cambiare il tipo a List o qualsiasi altra monade e non si verificheranno errori di compilazione (a causa della libreria di grandi librerie Scalas non si è obbligati a utilizzare solo operazioni che lavorare sulle monadi, ci sono molti metodi definiti su molti tipi).

+1

Mi piace questo ragionamento. Aggiungo anche che con foreach non si introduce mai 'get()' nel proprio codice, il che è una cattiva pratica con un 'Option' anche quando si testano correttamente' nonEmpty() '. Se devi fare qualcosa indipendentemente dal fatto che l'opzione sia vuota, 'match' con casi' Some (x) 'e' None' è la strada da percorrere. – pauljm

9

foreach ha senso, se si pensa di Option come un List, ma con un massimo di un elemento.

uno stile più ordinato, IMO, è quello di utilizzare la comprensione per-:

for (o <- opt) { 
    something(o) 
} 
5

foreach ha un senso se si considera Option essere un elenco che può contenere al massimo un singolo valore. Ciò porta anche a una corretta intuizione su molti altri metodi disponibili per Option.

mi viene in mente almeno un motivo importante che si potrebbe desiderare di preferire foreach in questo caso: elimina eventuali errori di runtime. Con l'approccio nonEmpty, sarete a un certo punto devi fare una get *, che può mandare in crash il programma di spettacolare se per caso dimentica di verificare la presenza di vuoto una sola volta.

Se si cancella completamente get dalla propria mente per evitare errori di questo tipo, un effetto collaterale è che si ha anche meno bisogno di nonEmpty! E inizierai a divertirti con foreach e lascia che il compilatore si occupi di cosa dovrebbe accadere se lo Option risulta vuoto.

vedrete questo concetto a saltar fuori in altri luoghi. Lei non avrebbe mai fare

if (age.nonEmpty) 
    Some(age.get >= 18) 
else 
    None 

Cosa si vede è piuttosto

age.map(_ >= 18) 

Il principio è che si vuole evitare di dover scrivere codice che gestisce il caso fallimento - si vuole scaricare tale onere a il compilatore. Molti vi diranno che si dovrebbe mai uso get, però attenzione si pensa che si sta pre-verifica. Questo rende le cose più facili.


* A meno che non si vuole realmente solo sapere se il Option contiene un valore e non mi interessa per il valore, nel qual caso nonEmpty è solo bene. In tal caso serve come una sorta di toBoolean.

+0

Nel tuo primo snippet di codice, non dovrebbe essere 'Some (age.get> = 18)' avere un 'Option' come il tipo risultante? – Beryllium

+0

@Beryllium Certo che dovrebbe! Non ho idea di come sia successo. Probabilmente da troppo Python. – kqr

1

Le cose non ho trovato in altre risposte:

  1. Quando si utilizza if, preferisco if (opt isDefined) a if (opt nonEmpty) come l'ex è meno raccolta-like e rende più chiaro che abbiamo a che fare con un opzione, ma potrebbe essere una questione di gusti.

  2. if e foreach sono differenti nel senso che if è un'espressione che restituisce un valore, mentre foreach rendimenti Unit. Quindi, in un certo modo, l'uso di foreach è ancora più simile a java di if, poiché java ha un ciclo foreach e Java if non è un'espressione. Usare una comprensione è più simile a scala.

  3. È inoltre possibile utilizzare pattern matching (ma questo è anche anche meno idiomatica)

  4. È possibile utilizzare il metodo fold, che prende due funzioni in modo da poter valutare un'espressione nel caso Some e un altro nel None caso. Potrebbe essere necessario specificare esplicitamente il tipo previsto a causa di come funziona l'inferenza di tipo (come mostrato here). Quindi, a mio parere, a volte può essere ancora più chiaro utilizzare entrambi i pattern matching o val result = if (opt isDefined) expression1 else expression2.

Se non è necessario un valore di ritorno e davvero non hanno bisogno di gestire il caso non è definita, è possibile utilizzare foreach.

+0

"Per quanto ne so non esiste un metodo Option che valuti un'espressione nel caso Some e un altro nel caso None" C'è: 'opt.fold (" None ") (v => s" Some ($ v) ")'. –

+0

Infatti, adattato la risposta, grazie! – herman

Problemi correlati