如何转换 List[ValidationNel[A, Option[B]]]

How to transform List[ValidationNel[A, Option[B]]]

我需要一个组合器将 List[ValidationNel[A, Option[B]]] 转换为 ValidationNel[A, List[B]] 以对验证进行排序,如果成功,则将这些废话弄平。

这是代码:

  def sequenceAndFlatten[A,B](valid: List[ValidationNel[A, Option[B]]]) : ValidationNel[A, List[B]] =
    valid.sequenceU.map(_.flatten)

有没有更好的方法?

我们可以使用 traverseM :

  • 引用 traverseM 文档(joinflatten 的 Scalaz 名称):

    A version of traverse where a subsequent monadic join is applied to the inner result.

  • sequence 等同于 traverse(identity).

所以 traverseM(identity) 应该类似于 sequence.map(_.flatten),但是 flatten 使用从 OptionList 的隐式转换,而 Scalaz 只会展平单子相同的类型,所以我们需要将 Option 显式转换为 ListtraverseM(_.map(_.toList)).

使用 traverseM 的一个问题是我们不能像使用 sequenceU 一样使用 Unapply。我们需要给编译器一些帮助并自己指定类型参数(使用类型 lambda)。

import scalaz.ValidationNel
import scalaz.std.list._
import scalaz.syntax.traverse._

def seqFlat[A,B](valid: List[ValidationNel[A, Option[B]]]): ValidationNel[A, List[B]] =
  valid.traverseM[({type l[x] = ValidationNel[A, x]})#l, B](_.map(_.toList))

这与您的 sequenceAndFlatten 得到相同的结果:

import scalaz.std.option._
import scalaz.syntax.std.option._
import scalaz.syntax.validation._

val validations = 
  List(1.some.successNel[String], 2.some.successNel, none[Int].successNel)

sequenceAndFlatten(validations)
// scalaz.ValidationNel[String,List[Int]] = Success(List(1, 2))

seqFlat(validations)
// scalaz.ValidationNel[String,List[Int]] = Success(List(1, 2))