EitherT with multiple return types

我的未来我决定 提交于 2021-01-28 02:05:19

问题


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 Ls 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!