Ci sono due importanti differenze. In primo luogo, Option
restituirà un None
se il suo argomento è nullo:
scala> val x: Option[String] = Some(null)
x: Option[String] = Some(null)
scala> val y: Option[String] = Option(null)
y: Option[String] = None
Questo può essere utile, ma non è sempre quello che si vuole, e (altrettanto importante) suggerisce che c'è una ragionevole possibilità che l'argomento può essere nullo in alcuni casi, che può essere fuorviante.
Some
è più appropriato per i casi in cui si desidera produrre un valore Option
attorno a un valore che non è nullo. Sfortunatamente la seconda differenza è che il tipo di ritorno di Some[Whatever]
, , non è , che può essere davvero scomodo in alcune situazioni in cui avere Some
dedotto significa che si otterranno errori di tipo quando si tenta di utilizzare None
o Option
in determinate posizioni in seguito. In questi casi è necessario utilizzare Some(foo): Option[Whatever]
, che è piuttosto spiacevole.
Ad esempio, supponiamo di avere un elenco di stringhe che rappresentano (speriamo) numeri interi e che vogliamo analizzare e sommare. Vogliamo un None
se c'è un errore di analisi e un Some(total)
in caso contrario. Quanto segue è un modo abbastanza ragionevole per fare questo in un unico attraversamento utilizzando la libreria standard:
List("1", "2", "3").foldLeft(Some(0)) {
case (acc, item) => for {
t <- acc
n <- util.Try(item.toInt).toOption
} yield t + n
}
Solo che questo non funziona, si ottiene un errore di tipo:
<console>:10: error: type mismatch;
found : Option[Int]
required: Some[Int]
t <- acc
^
Siamo in grado di risolvere il problema questo scrivendo .foldLeft(Some(0): Option[Int])
, ma ugh.
Questo non è un problema nell'esempio specifico perché il tipo restituito è esplicitamente Option[Int]
, quindi non è necessario preoccuparsi dell'inferenza di tipo. In tal caso, Some(a)
è la scelta giusta.
Come nota a margine, Scalaz fornisce some
e none
costruttori che consentono di evitare il problema di inferenza e soluzioni rumoroso come Some(foo): Option[Whatever]
:
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> some(10)
res0: Option[Int] = Some(10)
scala> none[Int]
res1: Option[Int] = None
Entrambi i tipi di ritorno sono Option
, che rende inferenza di tipo molto più facile. È banalmente possibile definire questi da soli se non si desidera utilizzare Scalaz:
scala> def some[A](a: A): Option[A] = Some(a)
some: [A](a: A)Option[A]
scala> def none[A]: Option[A] = None
none: [A]=> Option[A]
Se si utilizzano questi invece di Some
e None
non avete mai preoccuparsi di un tipo specifico di essere impropriamente dedotto.
Per riepilogare: utilizzare Option(foo)
solo in situazioni in cui l'argomento può essere nullo (che idealmente dovrebbe essere solo per cose come l'interoperabilità con Java).Utilizzare Some(foo)
nei casi in cui il valore è stato digitato esplicitamente come Option
. Se il tipo dedotto sarà Some[Whatever]
, aggiungere un'annotazione di tipo : Option[Whatever]
o utilizzare qualcosa come Scalaz some
.
Ci sono delle differenze nel loro comportamento: basta provare "Some [String] (null)" e "Option [String] (null)". –
Hai perfettamente ragione sulla loro inizializzazione e hai spiegato chiaramente questo caso utente nella tua risposta! Modificherò la mia risposta per essere accurata. – ipoteka