其他答案非常好,这个例子不像其他例子那么实用。我只想补充一点基础理论。
您所描述的通常称为函数式编程中的遍历。你有一个像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
改用了。)
如前所述,遍历不仅适用于Either
s,还适用于任何一元计算。因此,相同的方法可用于解决大量问题,例如对有状态计算进行排序(由 scalaz's 建模State
)、对非确定性计算进行排序(List
monad)等。