使用 EitherT 评估使用共享错误类型继承的操作结果?
Using EitherT to evaluate the results of operations using a shared error type inheritance?
我有一个剥香蕉的错误类型层次结构:
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)
EitherT
monad 转换器在 A
或 B
类型参数中不是协变的
case class EitherT[F[_], A, B]
与 Either
不同,后者在 A
和 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
但是我们可以安全地widen monad transformers
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
我有一个剥香蕉的错误类型层次结构:
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)
EitherT
monad 转换器在 A
或 B
类型参数中不是协变的
case class EitherT[F[_], A, B]
与 Either
不同,后者在 A
和 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
但是我们可以安全地widen monad transformers
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