问题描述
我想对所有返回 Either[Error,Item]
的操作序列进行验证它应该是快速失败的(在我最初的需要中),我的意思是,返回任一 [Error,Seq[Item]].如果出现错误,很明显我不希望执行以下操作.但将来我可能想收集所有错误,而不是只返回第一个.
I'd like to implement validation for a sequence of operations that all return Either[Error,Item]
It should be fail-fast (in my initial need), I mean, returning Either[Error,Seq[Item]].If there is an error, it's obvious i do not want the following operations to be performed.But in the future i may want to collect all the errors instead of returning only the first one.
我知道 Scalaz 可以完成这项工作,但现在我完全不了解 Scalaz 的所有部分,而且我很确定有一种更简单的方法可以在不使用 Scalaz 的情况下完成它,但例如使用按名称参数.
I know Scalaz can do the job but for now I quite don't understand all parts of Scalaz and I'm pretty sure there's a simpler way to do it without using Scalaz, but using by-name parameters for exemple.
有没有办法按名称存储序列中的参数?这样我就可以创建一系列代表我的操作的按名称值?
Is there a way to store by-name parameters in a sequence?So that i can create a sequence of by-name values that represent my operations?
我的意思是,某种类型 Seq[=>;要么[错误,项目]]
然后我可以做一些事情,比如调用 takeWhile 或 collectFirst 或类似的东西,而无需在创建序列之前执行所有操作?我希望这些操作仅在对序列进行迭代时执行.
I mean, some kind of type Seq[=> Either[Error,Item]]
Then I could do something like calling takeWhile or collectFirst or something somilar, without all the operations being performed before the creation of the sequence?I would expect the operations to be performed only when iterating on the sequence.
谢谢
推荐答案
你确实可以使用 Seq[() =>要么[Error, Item]]
在集合创建时推迟计算.所以例如
You can indeed use a Seq[() => Either[Error, Item]]
to defer the computation at collection creation time. So for example
val doSomething1: () => Either[Error, Item] = () => { println(1); Right(1) }
val doSomething2: () => Either[Error, Item] = () => { println(2); Right(2) }
val doSomething3: () => Either[Error, Item] = () => { println(3); Left("error") }
val doSomething4: () => Either[Error, Item] = () => { println(4); Right(3) }
val doSomething5: () => Either[Error, Item] = () => { println(5); Left("second error") }
val l = Seq(doSomething1, doSomething2, doSomething3, doSomething4, doSomething5)
(Item
s 是示例中的 Int
s 和 Error
s 是 String
s)
(Item
s are Int
s in the example and Error
s are String
s)
然后您可以使用以下递归函数在第一次失败时延迟停止处理它们:
Then you can process them lazily stopping at first failure using the following recursive function:
def processUntilFailure(l: Seq[() => Either[Error, Item]]): Either[Error, Seq[Item]] = {
l.headOption.map(_.apply() match {
case Left(error) => Left(error)
case Right(item) => processUntilFailure(l.tail).right.map(_ :+ item)
}).getOrElse(Right(Nil))
}
所以现在当我运行 processUntilFailure(l)
scala> processUntilFailure(l)
1
2
3
res1: Either[Error,Seq[Item]] = Left(error)
如果你想生成一个Either[Seq[String], Seq[Int]]
(处理所有操作).你可以做一点改变:
If you wanted to generate a Either[Seq[String], Seq[Int]]
(processing all the operations). You could do it with a little change:
def processAll(l: Seq[() => Either[Error, Item]]): Either[Seq[Error], Seq[Item]] = {
l.headOption.map(_.apply() match {
case Left(error) => processAll(l.tail) match {
case Right(_) => Left(Seq(error))
case Left(previousErrors) => Left(previousErrors :+ error)
}
case Right(item) => processAll(l.tail).right.map(_ :+ item)
}).getOrElse(Right(Nil))
}
您可以看到的唯一变化是模式匹配中的 Left 大小写.运行这个:
The only change as you can see is the Left case in the pattern match. Running this one:
scala> processAll(l)
1
2
3
4
5
res0: Either[Seq[Error],Seq[Item]] = Left(List(second error, error))
processAll
可以替换为 l
val zero: Either[Seq[Error], Seq[Item]] = Right(Seq[Item]())
l.foldLeft(zero) { (errorsOrItems: Either[Seq[Error], Seq[Item]], computation: () => Either[String, Int]) =>
computation.apply().fold(
{ (error: String) => Left(errorsOrItems.left.toOption.map(_ :+ error).getOrElse(Seq(error))) },
{ (int: Int) => errorsOrItems.right.map(_ :+ int) })
}
processUntilFailure
也可以,但并不容易.因为从折叠中提前中止是很棘手的.当您发现自己需要这样做时,这里有一个关于其他可能方法的好答案.
processUntilFailure
can as well but not easily. Since aborting early from a fold is tricky. Here's a good answer about other possible approaches when you find yourself needing to do that.
这篇关于在 Scala 中使用一系列按名称参数进行验证?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!