Zarówno cchantep i Marth są dobre rozwiązania dla najbliższej problem. Ale szerzej, jest to trudne do potraktowania Albo jako coś w pełni analogicznego do Option
, szczególnie jeśli pozwalasz wyrazić sekwencje potencjalnie nieudanych obliczeń dla zrozumienia. Albo ma API do projekcji (używane w rozwiązaniu cchantepa), ale jest nieco zepsuty. (Każda z projekcji włamuje się w celu zrozumienia ze strażnikami, dopasowaniem do wzorca lub przypisaniem zmiennych).
FWIW, Napisałem library, aby rozwiązać ten problem. To powiększa Albo z this API. Definiujesz "stronniczość" dla swoich Eitherów. "Prawidłowe odchylenie" oznacza, że zwykły przepływ (map, get, etc) jest reprezentowany przez obiekt Right
, podczas gdy obiekty Left
stanowią pewien problem. (Prawe odchylenie jest konwencjonalne, chociaż możesz także zdefiniować lewe odchylenie, jeśli wolisz.) Wtedy możesz traktować Either
jak Option
; to wykrywa w pełni analogiczny API.
import com.mchange.leftright.BiasedEither
import BiasedEither.RightBias._
val myEither:Either[String, Object] = ...
val o = myEither.getOrElse("Substitute")
Więcej pożytkiem można teraz traktować Albo jak prawdziwy monady Scala, czyli używać flatMap, mapa, filtr i za listowe:
val myEither : Either[String, Point] = ???
val nextEither = myEither.map(_.x) // Either[String,Int]
lub
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)
}
}
Jeśli wszystko kroki przetwarzania powiodły się, locPopPair
będzie Right[Long]
. Jeśli coś pójdzie nie tak, będzie to pierwszy napotkany błąd.
To trochę bardziej skomplikowane, ale dobrym pomysłem jest zdefiniowanie pustego tokena. Spójrzmy na lekkim odmianą dla zrozumienia powyżej:
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint(p) if p.x > 1000
} yield {
(p, g.population)
}
}
Co by było, gdyby test p.x > 1000
powiodło? Chcielibyśmy zwrócić pewną wartość Left
, która oznacza "puste", ale nie ma uniwersalnej właściwej wartości (nie wszystkie Left
są s są Left[String]
.Odtąd, co by się stało, kod rzuciłby NoSuchElementException
. Ale możemy określić pusty znak sami, jak poniżej:
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)
}
}
Teraz, jeśli test p.x > 1000
się nie powiedzie, nie będzie wyjątków, locPopPair
będzie tylko Left("EMPTY")
.
Wygląda na to, że 'foo' powinien zwrócić' String', a nie 'albo '. Czy nie brakuje Ci '.getOrElse (value)' na końcu? – Marth
"albo chcę zwrócić lewy, albo proces prawy" jest to, co zostało zrobione poprzez odwzorowanie właściwej projekcji. Dla 'Albo [A, B]' celem jest uzyskanie albo 'A' (pod warunkiem, że' B => A' dla 'Right') albo' B' (pod warunkiem, że 'A => B' dla' ' Left'), a następnie '.fold' jest twoim przyjacielem. – cchantep
Masz rację, zbyt szybko przeczytałem to pytanie. Po prostu spojrzałem na kod i pomyślałem, że pytanie brzmi: "jak to napisać bardziej zwięźle". Mój błąd. – Marth