[英]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.