具有多种 return 类型的 EitherT

EitherT with multiple return types

我正在尝试用 for-comprehension 和 EitherT 编写 futures,但由于 return 类型,我遇到了麻烦。请有人解释为什么这不能编译,我怎样才能通过改变 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
  }
}

在for-comprehension中,链中第一个步骤的类型和偏差决定了链中所有其余步骤的类型。因为 Either 是右偏的,我们只能在 for-comprehension 的步骤之间更改正确的类型,正如@Krzysztof 所建议的那样。例如,

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"

在您的情况下,第一步 EitherT(func1) 的类型是 EitherT[Future, L1, R1],因此接下来的步骤 EitherT(func2)EitherT(func3(f1, f2)) 必须具有以下形状的类型

EitherT[Future, L1, X]

其中只有 X 可以变化。让你的理解快乐的一种方法是从 L 中创建 代数数据类型 ,就像这样

  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)

这是一个有效的 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))
}

输出

Success(Right(R3(2)))