问题
I am trying to compose futures with for-comprehension and EitherT, but I am having trouble due the return types. Please can someone explain why this does not compile and how can I make it compile changing the for-comprehension?
import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import cats.data.EitherT
import cats.implicits._
object CatsApp extends App {
case class L1(a: String)
case class L2(a: String)
case class L3(a: String)
case class R1(num: Int)
case class R2(num: Int)
case class R3(num: Int)
def func1: Future[Either[L1, R1]] = {
if (true) Future(Right(R1(1)))
else Future(Left(L1("A")))
}
def func2: Future[Either[L2, R2]] = {
if (true) Future(Right(R2(1)))
else Future(Left(L2("A")))
}
def func3(a: R1, b: R2): Future[Either[L3, R3]] = {
if (true) Future(Right(R3(a.num + b.num)))
else Future(Left(L3("A")))
}
def comp = {
for {
f1 <- EitherT(func1)
f2 <- EitherT(func2)
f3 <- EitherT(func3(f1, f2))
} yield f3
}
}
回答1:
In a for-comprehension the type and the bias of the first step in the chain determines the type of all the rest of the steps in the chain have to be. Because Either
is right-biased, we can only change the right type between the steps of the for-comprehension, as advised by @Krzysztof. For example,
val e1: Either[String, Int] = Right(42)
val e2: Either[String, Char] = Right('A')
for {
num <- e1
char <- e2
} yield "I compile despite having different Rights"
In your case the type of first step EitherT(func1)
is EitherT[Future, L1, R1]
, hence the next steps EitherT(func2)
and EitherT(func3(f1, f2))
must have the following shape of the type
EitherT[Future, L1, X]
where only X
can vary. One way to make your for-comprehension happy is to create algebraic data types out of L
s like so
sealed abstract class L(val a: String)
final case class L1(s: String) extends L(s)
final case class L2(s: String) extends L(s)
final case class L3(s: String) extends L(s)
Here is a working example
object CatsApp extends App {
sealed abstract class L(val a: String)
final case class L1(s: String) extends L(s)
final case class L2(s: String) extends L(s)
final case class L3(s: String) extends L(s)
case class R1(num: Int)
case class R2(num: Int)
case class R3(num: Int)
def func1: Future[Either[L, R1]] = {
if (true) Future(Right(R1(1)))
else Future(Left(L1("A")))
}
def func2: Future[Either[L, R2]] = {
if (true) Future(Right(R2(1)))
else Future(Left(L2("A")))
}
def func3(a: R1, b: R2): Future[Either[L, R3]] = {
if (true) Future(Right(R3(a.num + b.num)))
else Future(Left(L3("A")))
}
def comp: EitherT[Future, L, R3] = {
for {
f1 <- EitherT(func1)
f2 <- EitherT(func2)
f3 <- EitherT(func3(f1, f2))
} yield f3
}
comp.value.andThen(v => println(v))
}
which outputs
Success(Right(R3(2)))
来源:https://stackoverflow.com/questions/59620066/eithert-with-multiple-return-types