scalaz:如何在验证中处理不同的错误类型?

时间:2016-01-05 13:03:27

标签: scala validation scalaz

如果我有多个操作返回Validation[E, _]具有固定错误类型的内容,我可以在for-comprehension中使用它们。例如:

val things: Validation[E, (Int, Double)] = for {
  i <- getValidationOfInt
  d <- getValidationOfDouble
} yield (i, d)

如果错误类型不同,怎么办?假设我从HTTP读取并希望将字符串响应转换为Int

import scalaz._; import Scalaz._

object ValidationMixing {
  class HttpError

  def getFromHttp: Validation[HttpError, String] = ???
  def parseInt(json: String): Validation[Throwable, Int] =
    Validation.fromTryCatchNonFatal(Integer.parseInt(json))

  val intParsedFromHttp: Validation[Any, Int] = for {
    s <- getFromHttp
    i <- parseInt(s)
  } yield i
}

这是编译,但只是因为验证的错误类型是Any,是ThrowableHttpError的超类型。这不是非常有用。

我可以想出各种方式来表示比Any更有用的组合错误类型(例如Validation[Error1 \/ Error2, Result]来存储,Validation[String, Result]转换为错误消息等)但它们都有缺点。

有没有惯用的方法呢?

1 个答案:

答案 0 :(得分:1)

由于没有人有更好的主意,我会留下我的答案以供将来参考。

如评论中所述,最好的方法是创建错误层次结构:

trait GenericError {  /* some commond fields */}
case class MyNumericError(/* fields */)

然后在验证时使用leftMap来生成相应的错误:

Validation.fromTryCatchNonFatal(...).leftMap(t => MyNumericError(...))

这种方法有两个优点

  • 首先,您将始终拥有Validation[GenericError, T],因此在验证的左侧部分没有不同的类型
  • 第二,这有助于为开发人员和服务用户生成有意义的错误,同时请注意,在您有许多上下文信息的情况下生成错误有助于此过程。