Sia di cchantep e Marth di sono buone soluzioni al problema immediato. Ma più in generale, è difficile da trattare O come qualcosa di completamente analogo a Option
, in particolare nel permettere di esprimere sequenze di calcoli potenzialmente disponibili per le comprensioni. O ha un'API di proiezione (usata nella soluzione di cchantep), ma è un po 'rotta. (Entrambe le proiezioni si interrompono per le comprensioni con guardie, pattern matching o assegnazione variabile.)
FWIW, ho scritto un library per risolvere questo problema. Aumenta l'una con this API. Definisci un "pregiudizio" per i tuoi Either. "Right bias" significa che il flusso ordinario (map, get, ecc.) È rappresentato da un oggetto Right
mentre gli oggetti Left
rappresentano un qualche tipo di problema. (Il bias corretto è convenzionale, sebbene tu possa anche definire un bias sinistro, se preferisci.) Quindi puoi trattare lo Either
come un Option
; offende un'API completamente analoga.
import com.mchange.leftright.BiasedEither
import BiasedEither.RightBias._
val myEither:Either[String, Object] = ...
val o = myEither.getOrElse("Substitute")
Molto più utile, è ora possibile trattare entrambi come un vero Monade scala, vale a dire utilizzare flatMap, carta, filtri, e per comprensioni:
val myEither : Either[String, Point] = ???
val nextEither = myEither.map(_.x) // Either[String,Int]
o
val myEither : Either[String, Point] = ???
def findGalaxyAtPoint(p : Point) : Either[String,Galaxy] = ???
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint(p)
} yield {
(p, g.population)
}
}
Se tutto i passaggi di elaborazione sono riusciti, locPopPair
sarà un Right[Long]
. Se qualcosa è andato storto, sarà il primo Left[String]
incontrato.
È leggermente più complesso, ma è una buona idea definire un token vuoto. Diamo un'occhiata a una leggera variazione sul per la comprensione di cui sopra:
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint(p) if p.x > 1000
} yield {
(p, g.population)
}
}
Che cosa accadrebbe se il test p.x > 1000
fallito? Vorremmo restituire un po 'di Left
che significa "vuoto", ma non esiste un valore appropriato universale (non tutti gli Left
sono Left[String]
.A partire da ora, ciò che accadrebbe è che il codice genererebbe un NoSuchElementException
. Ma siamo in grado di specificare un token vuoto noi stessi, come di seguito:
import com.mchange.leftright.BiasedEither
val RightBias = BiasedEither.RightBias.withEmptyToken[String]("EMPTY")
import RightBias._
val myEither : Either[String, Point] = ???
def findGalaxyAtPoint(p : Point) : Either[String,Galaxy] = ???
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint(p) if p.x > 1000
} yield {
(p, g.population)
}
}
Ora, se il test p.x > 1000
fallisce, non ci sarà alcuna eccezione, locPopPair
sarà solo Left("EMPTY")
.
Sembra che 'foo' debba restituire un' String', non un 'O'. Non ti manca un '.getOrElse (valore)' alla fine? – Marth
"o voglio tornare a sinistra o elaborare a destra" è ciò che viene fatto mappando la proiezione giusta. Per un 'O [A, B]' l'obiettivo è ottenere un 'A' (fornito un' B => A' per 'Right') o un' B' (fornito un 'A => B' per' Sinistra'), quindi '.fold' è tuo amico. – cchantep
Hai ragione, ho letto la domanda troppo velocemente. Ho appena visto il codice e ho pensato che la domanda era "come posso scrivere in modo più sintetico". Colpa mia. – Marth