如何减少 return 具有短路行为的函数的 Seq?

How to reduce Seq of functions that return Either with short circuit behavior?

举个例子(adapted from the cats Either documentation).

正在按顺序应用三个验证函数,每个函数返回一个 Either。如果任何函数的应用失败,则返回 Left 值。

object Validators {
  def isBiggerThan5(i: Int): Either[Exception, Int] =
    if (i > 5) Either.right(i)
    else Either.left(new NumberFormatException(s"${i} is smaller than 5."))

  def isSmallerThan20(i: Int): Either[Exception, Int] =
    if (i < 20) Either.right(i)
    else Either.left(new NumberFormatException(s"${i} is bigger than 20."))

  def isEven(i: Int): Either[Exception, Int] =
    if (i % 2 == 0) Either.right(i)
    else Either.left(new NumberFormatException(s"${i} is not an even number."))
}
import Validators._

def magic(i: Int): Either[Exception, Int] =
  isBiggerThan5(i).flatMap(isSmallerThan20).flatMap(isEven)

现在假设三个验证函数作为参数传递:

type Ops = Int => Either[Exception, Int]

def magic(ops: Seq[Ops])(s: String): Either[Exception, String] =
  ??? // How to apply ops in sequence with short circuit behavior?

println(magic(Seq(isBiggerThan5, isSmallerThan20, isEven)))

如何评价Seq[Ops]一个接一个的短路行为?我玩过 Seq.foldLeftSeq.reduce 但不太明白。

我对错误累积不感兴趣。它应该在 returns 左

的第一个验证器上简单地失败。

foldLeftM 应该可以。

import cats.syntax.all._
def verify(i: Int, ops: Vector[Ops]): Either[Exception, Int] =
  ops.foldLeftM(i)((i, o) => o(i))

我同意最好的解决方案是使用 foldLeftM
但是,为了完整起见,如果您想要更 vanilla 的解决方案,这是 List 和尾递归的完美用例。

type Ops[A] = A => Either[Exception, A]

def magic[A](initial: A)(ops: List[Ops[A]): Either[Exception, A] = {
  @annotation.tailrec
  def loop(remaining: List[Ops[A]], current: A): Either[Exception, A] =
    remaining match {
      case step :: tail =>
        step(current) match {
          case Right(next) =>
            loop(remaining = tail, current = next)

          case left @ Left(_) =>
            left
        }

      case Nil =>
        Right(current)
    }

  loop(remaining = ops, current = initial)
}

(看完所有这些样板文件后,您就会开始欣赏 cats