使用Scalaz 7结合验证

7

给定以下函数:

def foo( a: A ): ValidationNEL[String,Seq[B]] = ...

def bar( b: B ): ValidationNEL[String,C] = ...

我希望将它们组合起来构建一个函数,该函数调用foo,然后在生成的Seq中对每个元素最终调用bar,以获得ValidationNEL[String,Seq[C]]

def fooAndBar( a: A ): ValidationNEL[String,Seq[C]]

Scalaz 7 的文档非常简短,我找不到任何相关的示例。

2个回答

5

对B序列进行深度遍历。请注意,我在这里使用了List,因为Scalaz 7似乎没有Seq的类型类实例,但如果确实需要,编写自己的实例也不应该太难。

import scalaz.{ValidationNEL, Traverse, NonEmptyList}
import scalaz.std.list.listInstance
case class A(a: Int)
case class B(b: Int)
case class C(c: Int)

def foo( a: A ): ValidationNEL[String,List[B]] = Validation.success(List(B(1), B(2)))
def bar( b: B ): ValidationNEL[String,C] = Validation.failure(NonEmptyList("error in " + b.b))//Validation.success(C(b.b * 2))

type ValNEL[A] = ValidationNEL[String, A]

def foobar(a: A): ValidationNEL[String, List[C]] =
  foo(a) flatMap { bs =>
    Traverse[List].traverse[ValNEL, B, C](bs)(bar)
  }

val r: scalaz.ValidationNEL[String, List[C]] = foobar(A(3))

更新:还请查看宝贵的Haskellwiki Typeclassopedia


(此文涉及IT技术,讲述了一个网页链接)

奇怪,flatMap 似乎不是 Validation 的成员。但是有一个 bind 方法。是否需要进行某些隐式转换? - paradigmatic
@paradigmatic 我认为它最近被重命名为flatMap (https://github.com/scalaz/scalaz/commit/d1aabf31f8faa6fb6ef8dc172efdf68b945a5216)。我使用7-M1,已经有了flatMap。 - ron
我认为如果在技术上严格要求的话,验证(Validation)不应该具有flatMap/bind功能,因为它不是一个单子(Monad),但你可以通过转换成Either实现相同的效果,绑定它,然后再转回来。虽然这种快捷方式很方便。 - ron
1
作为一个小的侧面说明:你可以导入 scalaz.syntax.traverse._ 并且只需写 foo(a).flatMap(_.traverse[ValNEL, C](bar)) - Travis Brown
Travis: 确实。另外foo(a) flatMap (Traverse[List].traverse[ValNEL, B, C](_)(bar))也是可能的。 - ron
请注意,对于像我这样容易混淆的人来说,ValidationNEL已经更名为ValidationNel。 - Gavin

1

从语法上看,这不是一个坏主意,但使用Kleisli >==> 需要在作用域中有一个ValidationNELBind实例,目前可以通过使用Validation.validationMonad来引入。不幸的是,这会覆盖ValidationNEL的默认Applicative实例,因此遍历将无法累积所有错误 :/ - ron
不确定为什么会被踩,验证示例可以做到这一点。等我几天后用电脑给出一个确切的解决方案 - 除非我完全错了 ;) - Channing Walton
啊,发重帖了。我想我明白了。 - Channing Walton
拥有自定义的Bind实例可能会解决这个问题,但在语法上使用flatMap更加简便,而且自定义Bind实例会带来一些麻烦。 - ron

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接