在 Scala Cats Validated 中,如何组合有序验证

In Scala Cats Validated, how can combine ordered validations

https://gist.github.com/satyagraha/897e427bfb5ed203e9d3054ac6705704 我发布了一个 Scala Cats 验证场景,看起来很合理,但我还没有找到一个非常好的解决方案。

本质上,有一个两阶段验证,验证各个字段,然后调用 class 构造函数,这可能会由于内部检查而抛出(通常这可能不受我的控制来更改,因此异常处理代码)。我们希望在任何字段验证失败时不调用构造函数,但也希望将任何构造函数失败合并到最终结果中。 "Fail-fast"二相检查就在这里

这是一种 flatMap 问题,cats.data.Validated 框架似乎可以通过 cats.data.Validated#andThen 操作来处理。但是,正如您在代码中看到的那样,我找不到解决该问题的特别巧妙的方法。 cats.syntax.CartesianBuilder 上可用的操作数量非常有限,我不清楚如何使用 andThen 操作 link 它。

欢迎提出任何想法!注意有一个 Cats 问题 https://github.com/typelevel/cats/issues/1343 可能是相关的,不确定。

对于快速失败、链式验证,使用 EitherValidated 更容易。您可以轻松地从 Either 切换到 Validated,反之亦然,具体取决于您是否需要累积误差。

您的问题的一个可能解决方案是为 User 创建一个智能构造函数,其中 returns 和 Either[Message, User] 并将其与 Validated[Message, (Name, Date)].[=18= 一起使用]

import cats.implicits._
import cats.data.Validated

def user(name: Name, date: Date): Either[Message, User] = 
  Either.catchNonFatal(User(name, date)).leftMap(Message.toMessage)

// error accumulation -> Validated
val valids: Validated[Message, (Name, Date)] = 
  (validateName(nameRepr) |@| validateDate(dateDepr)).tupled

// error short circuiting -> either
val userOrMessage: Either[Message, User] =
  valids.toEither.flatMap((user _).tupled)

// Either[Message,User] = Right(User(Name(joe),Date(now)))

我会创建一个辅助二阶函数来包装抛出异常的函数:

def attempt[A, B](f: A => B): A => Validated[Message, B] = a => tryNonFatal(f(a))

另外,case 类 的默认 companion 扩展了 FunctionN trait,所以不需要做 (User.apply _).tupled,它可以缩短为 User.tupled(在自定义 companion 上,你需要写入 extends ((...) => ...))apply 覆盖将自动生成)

所以我们最终使用 andThen:

val valids = validateName(nameRepr) |@| validateDate(dateDepr)
val res: Validated[Message, User] = valids.tupled andThen attempt(User.tupled)