如何减少 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.foldLeft
或 Seq.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)
举个例子(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.foldLeft
或 Seq.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)