使用 scalaz 你有Validation[E, A]
,就像Either[E, A]
但有这样的性质:如果E
是一个半组(意味着可以连接的东西,如列表),而不是可以以保留所有发生的错误的方式组合多个经过验证的结果。
以 Scala 2.10-M6 和 Scalaz 7.0.0-M2 为例,其中 Scalaz 有一个自定义Either[L, R]
named \/[L, R]
默认情况下是右偏的:
import scalaz._, Scalaz._
implicit class EitherPimp[E, A](val e: E \/ A) extends AnyVal {
def vnel: ValidationNEL[E, A] = e.validation.toValidationNEL
}
def parseInt(userInput: String): Throwable \/ Int = ???
def fetchTemperature: Throwable \/ Int = ???
def fetchTweets(count: Int): Throwable \/ List[String] = ???
val res = (fetchTemperature.vnel |@| fetchTweets(5).vnel) { case (temp, tweets) =>
s"In $temp degrees people tweet ${tweets.size}"
}
Here result
is a Validation[NonEmptyList[Throwable], String]
,包含发生的所有错误(温度传感器错误和/或 Twitter 错误或无)或成功消息。然后您可以切换回\/
为了方便。
注意:Either 和 Validation 之间的区别主要在于,使用 Validation 可以累积错误,但不能累积错误flatMap
失去累积的错误,而使用 Either 你不能(轻易)累积但可以flatMap
(或在 for 理解中)并且可能会丢失除第一条错误消息之外的所有消息。
关于错误层次结构
我想您可能对此感兴趣。无论使用 scalaz/Either
/\/
/Validation
,我的经验是,入门很容易,但继续前进需要一些额外的工作。问题是,如何以有意义的方式从多个出错函数中收集错误?当然,你可以直接使用Throwable
or List[String]
到处都很轻松,但听起来不太有用或可解释。想象一下,得到一个错误列表,例如“儿童年龄缺失”::“读取文件时出现 IO 错误”::“除以零”。
所以我的选择是创建错误层次结构(使用 ADT-s),就像将 Java 的已检查异常包装到层次结构中一样。例如:
object errors {
object gamestart {
sealed trait Error
case class ResourceError(e: errors.resource.Error) extends Error
case class WordSourceError(e: errors.wordsource.Error) extends Error
}
object resource {
case class Error(e: GdxRuntimeException)
}
object wordsource {
case class Error(e: /*Ugly*/ Any)
}
}
然后,当使用具有不同错误类型的错误函数的结果时,我将它们加入到相关的父错误类型下。
for {
wordSource <-
errors.gamestart.WordSourceError <-:
errors.wordsource.Error <-:
wordSourceCreator.doCreateWordSource(mtRandom).catchLeft.unsafePerformIO.toEither
resources <-
errors.gamestart.ResourceError <-:
GameViewResources(layout)
} yield ...
Here f <-: e
映射函数f
在...的左边e: \/
since \/
是一个双函子。为了se: scala.Either
你可能有se.left.map(f)
.
这可以通过提供进一步改进无形的 http://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/monoids.scala HListIso
以便能够绘制漂亮的错误树。
修订
更新:(e: \/).vnel
将故障侧提升至NonEmptyList
因此,如果我们失败了,我们至少会遇到一个错误(是:或没有)。