【问题标题】:Scalaz: how can I accumulate Failures or apply a function to Validations with different types?Scalaz:如何累积失败或将函数应用于不同类型的验证?
【发布时间】:2013-06-05 01:57:03
【问题描述】:

我有 19 个字符串需要验证为各种类型。当所有验证成功后,我想实例化一个代表电子表格行的类(其中列的类型不同)。

当一个或多个字符串验证失败时,我希望将错误累积在 NonEmptyList 中。

如果有 12 个或更少的项目,我可以使用 |@|或申请12。如果我使用 for 表达式,它会快速失败并且不会发生累积。

当 for 表达式失败时,我可以对失败进行排序,但这意味着我要循环两次。有没有办法在累积所有失败的同时使用 scalaz 将每个验证成功放入一个变量中(如果我使用 for 表达式实例化类会发生这种情况)?

【问题讨论】:

  • 有点笨拙,不过你可以use <*> (or ap) directly,它会累积错误,并且对可以应用的次数没有任意限制。
  • 你不能只将map 的字符串列表转换为Validation,然后将partition 得到isFailure 的结果列表。

标签: validation scala scalaz


【解决方案1】:

假设我们有一个案例类(可能有十二个以上的成员):

case class Foo(a: Int, b: Char, c: Symbol, d: String)

我们将错误表示为字符串,并为方便起见定义了类型别名:

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

我们也有一些验证结果:

val goodA: ErrorOr[Int] = 1.success
val goodB: ErrorOr[Char] = 'a'.success
val goodC: ErrorOr[Symbol] = 'a.success
val goodD: ErrorOr[String] = "a".success

val badA:  ErrorOr[Int] = "x".failNel
val badC:  ErrorOr[Symbol] = "y".failNel

现在我们可以写了:

val foo = (Foo.apply _).curried

val good: ErrorOr[Foo] = goodD <*> (goodC <*> (goodB <*> (goodA map foo)))
val bad:  ErrorOr[Foo] = goodD <*> (badC  <*> (goodB <*> (badA  map foo)))

这给了我们想要的东西:

scala> println(good)
Success(Foo(1,a,'a,a))

scala> println(bad)
Failure(NonEmptyList(x, y))

在 Haskell 中,这将是 much prettier——你只需写:

Foo <$> goodA <*> goodB <*> goodC <*> goodD

不幸的是,Scala 较弱的类型推断要求我们以错误的顺序编写参数。

【讨论】:

  • 这对我有用,但最好不要有一大群近亲。 :)
  • 请注意,我已经用Shapeless 写了一些替代方案a blog post
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-13
  • 2023-03-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-05
相关资源
最近更新 更多