使用EitherT来评估使用共享错误类型继承的操作结果?

我有一个用于剥香蕉的错误类型层次结构:

sealed trait PeelBananaError
object PeelBananaError {
  case object TooRipe extends PeelBananaError
  case object NotRipeEnough extends PeelBananaError
}

我有一些结果EitherT,我们知道只能以两种方式之一失败:

val peelBrownBananaResult: EitherT[Future, TooRipe, String] = ...
val peelGreenBananaResult: EitherT[Future, NotRipeEnough, String] = ...

现在我需要String从右边收集结果并将它们组合起来以获得最终结果:

val combinedResult: EitherT[Future, PeelBananaError, String] = for {
  first <- peelBrownBananaResult
  second <- peelGreenBananaResult
} yield (first + second)

但是尝试这个会给我一个编译错误:

cmd15.sc:2: inferred type arguments [PeelBananaError.NotRipeEnough.type,String] do not conform to method flatMap's type parameter bounds [AA >: PeelBananaError.TooRipe.type,D]
  first <- peelBrownBananaResult
           ^
cmd15.sc:2: type mismatch;
 found   : String => cats.data.EitherT[scala.concurrent.Future,PeelBananaError.NotRipeEnough.type,String]
 required: String => cats.data.EitherT[scala.concurrent.Future,AA,D]
  first <- peelBrownBananaResult
        ^
Compilation Failed

似乎编译器无法推断这些Left类型共享公共继承,PeelBananaError因此 for comprension 无法编译。这里有什么解决方法吗?我可以给编译器一些提示,以便我可以运行它来理解?

我尝试了以下方法,但使用.asInstanceOf似乎很hacky,结果代码看起来很丑陋,这真的是唯一的解决方案吗?

val combinedResult: EitherT[Future, PeelBananaError, String] = for {
  first <- peelBrownBananaResult.asInstanceOf[EitherT[Future,PeelBananaError,String]]
  second <- peelGreenBananaResult.asInstanceOf[EitherT[Future,PeelBananaError,String]]
} yield (first + second)

回答

EitherTmonad 转换器在AB类型参数中不是协变的

case class EitherT[F[_], A, B]

不同于EitherA和中协变的B

sealed abstract class Either[+A, +B]

这就是为什么Either对超类型进行类型检查的原因

Either.left(TooRipe): Either[PeelBananaError, String] // ok

EitherT

EitherT.left(Future(TooRipe)): EitherT[Future, PeelBananaError, String] // error

但是我们可以安全地扩展monad 转换器

for {
  first <- peelBrownBananaResult.leftWiden[PeelBananaError]
  second <- peelGreenBananaResult.leftWiden[PeelBananaError]
} yield (first + second)
// : EitherT[Future, PeelBananaError, String] = ...

不像不安全的扩大 asInstanceOf

type T = Either[TooRipe.type, String]
val a = Either.left[PeelBananaError, String](new PeelBananaError {})
val b: Either[TooRipe.type, String] = a.asInstanceOf[T] // run-time error
val c: Either[TooRipe.type, String] = a.widen[T]        // compile-time error


以上是使用EitherT来评估使用共享错误类型继承的操作结果?的全部内容。
THE END
分享
二维码
< <上一篇
下一篇>>