其他答案非常好,这个例子不像其他例子那么实用。我只想补充一点基础理论。
您所描述的通常称为函数式编程中的遍历。你有一个像Seq[X]和一个单子(或应用)计算的集合X => M[Y]。标准map给你Seq[M[Y]],但遍历给你M[Seq[Y]]。
在这种情况下,一元计算是产生 的东西Either[Error,Right],在这种情况下M[_]是Either[Error,_]。所以如果你只是用这样一个函数映射一个集合,你会得到Seq[Either[Error,Right]]. 但你想要的是Either[Error,Seq[Right]],这正是遍历所做的。如果函数在序列的任何元素上失败(返回Left(something)),那么最终结果就是 this Left(something)。如果函数在所有元素上都成功(返回所有元素Right(...)),那么最终结果是Right(sequenceOfResults).
Scala 没有内置函数,但Scalaz有,它被称为traverse. 一个完整的例子:
import scalaz._;
import Scalaz._;
import Applicative._;
object RightMatch extends App {
// our example function
def foo(s: String): Either[String,Int] =
if (s.startsWith("a")) Right(s.length)
else Left("wrong: " + s);
// We make an utility function for traversing Sequences wit Eithers:
def traverseRight[X,L,R](es: Seq[X], f: X => Either[L,R]): Either[L,Seq[R]] = {
// we need to convert Either to Either.RightProjection
type RightF[Y] = Either.RightProjection[L,Y];
es.traverse[RightF,R](x => f(x).right).e; // and back to Either
}
// Or, if we just want to convert an existing sequence of eithers:
def traverseRight[L,R](es: Seq[Either[L,R]]): Either[L,Seq[R]] =
traverseRight(es, identity[Either[L,R]]);
{
val a = Seq("a", "ab", "ac");
traverseRight(a, foo) match {
case Right(arr) => println(arr); // we get the array of Ints here
case Left(err) => println(err); // we get String here (the first error)
}
}
}
(请注意,在 ScalazArray中没有实现可遍历(我不知道为什么),所以我Seq改用了。)
如前所述,遍历不仅适用于Eithers,还适用于任何一元计算。因此,相同的方法可用于解决大量问题,例如对有状态计算进行排序(由 scalaz's 建模State)、对非确定性计算进行排序(Listmonad)等。