2015-12-31 10 views
8

È possibile gestire Either in modo simile a Option? In Option, ho una funzione getOrElse, in Either Voglio restituire Left o elaborare Right. Sto cercando il modo più veloce di fare questo senza alcun tipo di piastra:Scala Either map Right or return Left

val myEither:Either[String, Object] = Right(new Object()) 
myEither match { 
    case Left(leftValue) => value 
    case Right(righValue) => 
     "Success" 
} 

risposta

1

Immagino che tu possa fare come segue.

def foo(myEither: Either[String, Object]) = 
    myEither.right.map(rightValue => "Success") 
+0

Sembra che 'foo' debba restituire un' String', non un 'O'. Non ti manca un '.getOrElse (valore)' alla fine? – Marth

+0

"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

+0

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

3

È possibile utilizzare .fold:

scala> val r: Either[Int, String] = Right("hello") 
r: Either[Int,String] = Right(hello) 

scala> r.fold(_ => "got a left", _ => "Success") 
res7: String = Success 

scala> val l: Either[Int, String] = Left(1) 
l: Either[Int,String] = Left(1) 

scala> l.fold(_ => "got a left", _ => "Success") 
res8: String = got a left 

Edit:

Rileggendo la tua domanda non è chiaro per me se si desidera restituire il valore nella Left o di un altro (definito altrove)
Se è il primo, è possibile passare identity a .fold, ho Wever questo potrebbe cambiare il tipo di ritorno a Any:

scala> r.fold(identity, _ => "Success") 
res9: Any = Success 
4

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").

Problemi correlati