将 ValidationNel[A, List[B]] 转换为 ValidationNel[A, NonEmptyList[B]]

Convert a ValidationNel[A, List[B]] into ValidationNel[A, NonEmptyList[B]]

我编写了一个组合器,当 List 为空时,将 ValidationNel[A, List[B]] 转换为 ValidationNel[A, NonEmptyList[B]],即 returns 和 Failure

  def nonEmpty[A, B](valid : ValidationNel[A, List[B]], fail : => A) : ValidationNel[A, NonEmptyList[B]] =
    valid match {
      case Failure(f) => f.failure[NonEmptyList[B]]
      case Success(s) =>
        if (!s.isEmpty) NonEmptyList(s.head, s.tail:_*).successNel[A]
        else fail.failureNel[NonEmptyList[B]]
    }

有没有更好的方法?

我们可以通过两种方式改进您的 nonEmpty 功能:

  • Scalaz 可以将 List 变成 Option[NonEmptyList] 使用 toNel.
  • 您在 ValidationNel 上的模式匹配类似于 flatMap,但 Validation 没有 flatMap 操作,因为它是 Applicative 而不是 Monad。 Scalaz 提供了一些简单的函数来在 Validation\/ 之间进行转换(也称为析取),因此我们可以使用 \/flatMap.

你的 nonEmpty 函数看起来像:

import scalaz.{ValidationNel, NonEmptyList}
import scalaz.syntax.std.list._
import scalaz.syntax.std.option._
import scalaz.syntax.nel._

def nonEmpty[A, B](valid: ValidationNel[A, List[B]], 
                   fail: => A): ValidationNel[A, NonEmptyList[B]] =
  valid.disjunction.flatMap(_.toNel toRightDisjunction fail.wrapNel).validation

您可以将其用作:

import scalaz.syntax.validation._

nonEmpty(List(1,2).success, "test")
// scalaz.ValidationNel[String,scalaz.NonEmptyList[Int]] = 
//   Success(NonEmptyList(1, 2))

nonEmpty(List().success, "list is empty")
// scalaz.ValidationNel[String,scalaz.NonEmptyList[Nothing]] =
//   Failure(NonEmptyList(list is empty))