【问题标题】:How to compose functions that return Validation?如何编写返回验证的函数?
【发布时间】:2015-06-02 00:25:00
【问题描述】:

这是我之前question的后续行动

假设我有两个验证函数,如果输入有效则返回输入,否则返回错误消息。

type Status[A] = ValidationNel[String, A]

val isPositive: Int => Status[Int] = 
  x => if (x > 0) x.success else s"$x not positive".failureNel

val isEven: Int => Status[Int] = 
  x => if (x % 2 == 0) x.success else s"$x not even".failureNel

还假设我需要验证case class X 的实例:

case class X(x1: Int, // should be positive 
             x2: Int) // should be even

更具体地说,我需要一个函数checkX: X => Status[X]。此外,我想将checkX 写成isPositiveisEven组合

val checkX: X => Status[X] =
  ({x => isPositive(x.x1)} |@| {x => isEven(x.x2)}) ((X.apply _).lift[Status])

这有意义吗?
您如何将checkX 写为isPositiveisEven组合

【问题讨论】:

  • 我现在要开始学习单子了,因为你手头有一个单子(教科书的例子)。
  • 我对单子有点了解。我认为我在这里不需要单子。你到底是什么单子?
  • Status (Validation) 是一个应用函子,但不是一个单子。
  • @n.m. 非常 简短回答:Validation 不是 monad,因为它在 scalaz 中以这种方式定义。有关更多详细信息,请参阅例如 stackoverflow.com/questions/12211776/…
  • @n.m. Validation 未定义为单子出于某种原因。欢迎您在任何 scalaz 教程中阅读它或在 SO 上提问。

标签: scala validation scalaz applicative


【解决方案1】:

有很多方法可以写这个,但我喜欢以下:

val checkX: X => Status[X] = x => isPositive(x.x1).tuple(isEven(x.x2)).as(x)

或者:

val checkX: X => Status[X] =
  x => isPositive(x.x1) *> isEven(x.x2) *> x.point[Status]

关键是您只想针对它们的“效果”运行这两个验证,然后在新的上下文中返回原始值。正如您自己的实现所示,这是一个完全合法的应用程序操作。只是有一些更好的写法。

【讨论】:

  • 请注意,如果您想使用经过验证的值(如在您的实现中),您可以这样写:x => (isPositive(x.x1) tuple isEven(x.x2)).map((X.apply _).tupled)。不过,我发现 as(x) 版本更清晰。
【解决方案2】:

这没有意义,因为这两个函数不能真正组合起来。您(大概)想检查x 是否为正x 是否为偶数 - 但在它既为负又为奇数的情况下,您希望收到这两个错误.但这永远不会作为这两个函数的组合发生 - 一旦应用任何一个失败案例,您就没有 x 可以传递给第二个函数了。

根据我的经验,Validation 几乎从来都不是正确使用的类型,正因如此。

如果您希望结果为成功或第一个错误的“快速失败”行为,您应该使用\/(在这种情况下为type Status[A] = String \/ A)。如果你想“在值旁边累积所有错误消息”行为,你需要Writer,即type Status[A] = Writer[Vector[String], A]。这两种类型都允许使用例如简单的组合(因为它们有可用的 monad 实例)。 Kleisli:Kleisli(isPositive) >==> isEven 适用于 Status 的这些定义中的任何一个(但不适用于您的)。

【讨论】:

  • 我的checkX确实累积错误。您可以在 REPL 中尝试checkX(new X(-1, 1)) 并查看结果Status[X] = Failure(NonEmptyList(-1 not positive, 1 not even))
  • 我不想在这个例子中使用单子。 Applicative(不是 monad)似乎更适合,
  • 我同意过度使用 Validation 很容易,但是有很多合法的用例,这听起来像是其中之一。我不明白第一段中的反对意见如何适用于此。
  • 啊-我错过了您将函数应用于不同的xs-在我的辩护中,您说的是“组合”,在我看来,这意味着将一个函数应用于另一个函数的结果。在这种情况下,我认为“优雅”的解决方案是使用一个无形的Poly,它知道如何分别处理每个字段(即case class X(x1: Int @@ WidgetCount, x2: Int @@ FooCheck),然后在Poly 中有两种不同类型的案例),然后在案例类中使用 shapeless'traverse。 (但有很多机器可能不值得)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-25
  • 1970-01-01
  • 2020-06-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多