La firma di getOrElse
per un LeftProjection[A, B]
è:
def getOrElse[AA >: A](or: ⇒ AA): AA
vale a dire che si aspetta che il argomento per essere di tipo AA
che è un supertipo di A
.
Nel primo esempio, hai omesso le annotazioni di tipo, consentendo al compilatore di dedurre Nothing
per A
. Quindi, hai fornito un argomento di tipo LeftProjection[Nothing, Int]
.
Perché Nothing
è un sottotipo di tutti i tipi , LeftProjection[Nothing, Int]
è banalmente un supertipo! Questo caso speciale nel sistema di tipi significa che è stato controllato quasi per caso.
Tuttavia, il supertipo più specifico di String
e LeftProjection[String, Int]
è Serializable
.
Quindi, se si vuole catena Either
s, è necessario un metodo che può prendere un altro Either[A, B]
, non solo un A
o B
.
Il metodo sembra che tu voglia sarebbe simile a questa:
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
}
(Si potrebbe analogamente scrivere rightOrElse
, che è un caso d'uso più comune.)
Questo diventa sintatticamente un po 'più utilizzabile se lo fai un metodo di estensione, usando impliciti.
implicit class EitherOps[A, B](e1: Either[A, B]) {
def leftOrElse(e2: => Either[A, B]): Either[A,B] = // as above
}
Perché questo si aspetta Either[A, B]
per entrambi gli operandi, invece di A
o B
(o qualche supertipo della stessa), è possibile concatenare le vostre Either
s.
scala> Right[String, Int](2) leftOrElse Right[String, Int](4) leftOrElse Left[String, Int]("Error")
res1: Either[String,Int] = Left(Error)
Questo sembra un modo particolarmente oscuro per ottenere un comportamento di caduta abbastanza semplice. Perché non scriverlo in modo diverso invece di provare a calzare il tuo comportamento desiderato in questi metodi? –