Opzione Monade a Scala
-
28-09-2019 - |
Domanda
Come ha lo scopo di Opzione lavoro monade? Sto navigando il Scala api e non v'è un esempio (intendo il secondo),
A causa di come per le opere di comprensione, se viene restituito None dalle request.getParameter, l'intero risultato di espressione a None
Ma quando provo questo codice:
val upper = for {
name <- None //request.getParameter("name")
trimmed <- Some(name.trim)
upper <- Some(trimmed.toUpperCase) if trimmed.length != 0
} yield upper
println(upper.getOrElse(""))
ottengo un errore di compilazione. Come è questo dovrebbe funzionare?
Soluzione
Si ottiene un errore di compilazione a causa di questo
name <- None
In questo modo, il tipo di None
è impostato None.type
e la name
variabile costituirebbero dunque essere di tipo Nothing
. (Cioè, sarebbe avere questo tipo se è realmente esistito, ma, ovviamente, il per la comprensione non ha nemmeno arrivare a crearlo in fase di esecuzione.) Esiste quindi nessun metodo name.trim
e non verrà compilato.
Se si ha a disposizione request.getParameter("name")
, il suo tipo sarebbe Option[String]
, name
potrebbe potenzialmente avere tipo String
e name.trim
sarebbe la compilazione.
E 'possibile ovviare a questo specificando il tipo di None
:
name <- None: Option[String]
Altri suggerimenti
Per espandere sulla risposta di Kevin, è possibile evitare i valori wrapping Some()
utilizzando l'operatore =
anziché l'operatore <-
:
val upper = for {
name <- None: Option[String] //request.getParameter("name")
trimmed = name.trim
upper = trimmed.toUpperCase if trimmed nonEmpty
} yield upper
Il per-di comprensione verrà compilato a qualcosa di molto simile alla versione di Kevin, ma spesso mi trovo più chiaro da usare map
e filter
esplicitamente evitare confusione (ad esempio i nomi delle variabili in più) che non aggiungono nulla al contenuto semantico dell'espressione.
Per espandere sulla risposta di Debilski, anche voi non c'è bisogno di avvolgere in modo esplicito i valori successivi Some()
, l'unico valore che si sta effettivamente la mappatura sopra è il name
originale.
Un approccio migliore sarebbe essere quello di utilizzare le operazioni map
e filter
direttamente invece di una per-di comprensione:
Nota: dietro le quinte, il compilatore Scala convertirà un per-la comprensione di una combinazione di carta / flatMap / filtro in ogni caso, per cui questo approccio non sarà mai meno efficiente di un per-comprensione, e può ben essere più efficiente
def clean(x:Option[String]) = x map { _.trim.toUpperCase } filterNot { _.isEmpty }
val param = None : Option[String] // request.getParameter("name")
println( clean(param).getOrElse("") )