繁体   English   中英

通过要么/分离Scala处理多种错误类型

[英]Handling multiple error types via either/disjunction Scala

我有 3 个不同的模块,每个模块都有自己的错误类型。 以下是一个非常简化的版本。

object ModuleA {
  case class ErrorA(msg: String)
  def getA: ErrorA \/ String = "1".right
}

object ModuleB {
  case class ErrorB(msg: String)
  def getB(s: String): ErrorB \/ Int = 1.right
}

object ModuleC {
  case class ErrorC(msg: String)
  def getC(s: String, i: Int): ErrorC \/ Long = 1L.right
}

作为这些模块的客户,链接这些调用的最佳方式是什么。

首先 - 深度嵌套的复杂返回类型,但具有所需的所有类型。

def call1: ModuleA.ErrorA \/ (ModuleB.ErrorB \/ (ModuleC.ErrorC \/ Long)) = {
  ModuleA.getA.map { s =>
    ModuleB.getB(s).map { i =>
      ModuleC.getC(s, i)
    }
  }
}

第二 - 非常易读,但错误类型丢失(推断的返回类型是Product \\/ Long )。 理想情况下会想要与错误类型类似的东西

def call2  =
  for {
    s  <- ModuleA.getA
    i  <- ModuleB.getB(s)
    l  <- ModuleC.getC(s, i)
  } yield l

第三 - 定义新的错误类型来封装现有的错误类型。 这对于不同的组合似乎不可行

最后,尝试使用EitherT,但似乎变得复杂

考虑创建一个代数数据错误的打出来,用于例如

sealed abstract class Error(val message: String)
case class ErrorA(msg: String) extends Error(msg)
case class ErrorB(msg: String) extends Error(msg)
case class ErrorC(msg: String) extends Error(msg)

然后将返回的\\/的左侧更改为Error

import scalaz.\/
import scalaz.syntax.either._

object ModuleA {
  def getA: Error \/ String = "1".right
}

object ModuleB {
  def getB(s: String): Error \/ Int = ErrorB("boom").left
}

object ModuleC {
  def getC(s: String, i: Int): Error \/ Long = 1L.right
}

for {
  s  <- ModuleA.getA
  i  <- ModuleB.getB(s)
  l  <- ModuleC.getC(s, i)
} yield l

这使

res0: Error \/ Long = -\/(ErrorB(boom))

如果无法创建 ADT,请考虑leftMap将错误类型更改为常见类型,如下所示

case class ErrorWrapper(m: String)

for {
  s  <- ModuleA.getA.leftMap { e: ModuleA.ErrorA => ErrorWrapper(e.msg) }
  i  <- ModuleB.getB(s).leftMap { e: ModuleB.ErrorB => ErrorWrapper(e.msg) }
  l  <- ModuleC.getC(s, i).leftMap { e: ModuleC.ErrorC => ErrorWrapper(e.msg) }
} yield l
// res0: ErrorWrapper \/ Long = -\/(ErrorWrapper(boom))

或者甚至,不寻常地,通过结构类型

implicit class CommonErrorWrapper[A <: Product { def msg: String }](e: A) {
  def toErrorWrapper: ErrorWrapper = ErrorWrapper(e.msg)
}

for {
  s  <- ModuleA.getA.leftMap(_.toErrorWrapper)
  i  <- ModuleB.getB(s).leftMap(_.toErrorWrapper)
  l  <- ModuleC.getC(s, i).leftMap(_.toErrorWrapper)
} yield l
// res1: ErrorWrapper \/ Long = -\/(ErrorWrapper(boom))

leftMap不仅对于改变错误类型很有用,而且我们可以通过添加本地可用的上下文信息来丰富错误。


注意EitherT单子变压器时类型的形状,可以使用F[A \\/ B]例如, Future[Error \\/ B]然而,在你的情况下,它仅仅是A \\/ B ,因此EitherT是可能不是正确的工具。 相关问题具有多种返回类型的EitherT

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM