Podpis getOrElse
dla LeftProjection[A, B]
jest:
def getOrElse[AA >: A](or: ⇒ AA): AA
czyli oczekuje argument musi być pewnego rodzaju AA
, który jest nadtypem A
.
W pierwszym przykładzie pominięto adnotacje typu, pozwalając kompilatorowi wywnioskować Nothing
dla A
. Następnie podałeś argument typu LeftProjection[Nothing, Int]
.
Ponieważ Nothing
jest podtypem wszystkich typów, LeftProjection[Nothing, Int]
jest trywialnie supertypem! Ten szczególny przypadek w systemie typu oznacza, że został on sprawdzony prawie przypadkowo.
Jednak najbardziej charakterystycznym typowym nadtypem String
i LeftProjection[String, Int]
jest Serializable
.
Tak więc, jeśli chcesz łańcucha Either
s, trzeba metodę, która może podjąć inną Either[A, B]
, a nie tylko A
lub B
.
Metoda wydaje się chcieć będzie wyglądać następująco:
def leftOrElse[A, B](e1: Either[A, B], e2: => Either[A, B]): Either[A,B] =
e1 match {
case Left(a) => Left(a)
case Right(b) => e2
}
(Można podobnie napisać rightOrElse
, który jest bardziej powszechne przypadek użycia).
Staje składniowo nieco bardziej użyteczny, jeśli czynisz z niej metodę rozszerzenia, używając implicite.
implicit class EitherOps[A, B](e1: Either[A, B]) {
def leftOrElse(e2: => Either[A, B]): Either[A,B] = // as above
}
Ponieważ oczekuje Either[A, B]
dla obu argumentów, zamiast A
lub B
(lub pewne jego supertypem), można łańcuch swoich Either
jesteś ty.
scala> Right[String, Int](2) leftOrElse Right[String, Int](4) leftOrElse Left[String, Int]("Error")
res1: Either[String,Int] = Left(Error)
Wygląda to na szczególnie niejasny sposób na uzyskanie dość prostego zachowania polegającego na upadku. Dlaczego po prostu nie napisać tego inaczej, niż próbować dopasować pożądane zachowanie do tych metod? –