他の回答は非常に優れており、この例は他の回答ほど実用的ではありません。基礎となる理論を少し追加したいと思います。
あなたが説明していることは、関数型プログラミングではトラバーサルと呼ばれることがよくあります。のようなコレクション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
には traversable の実装がないことに注意してください (理由はわかりません)、Seq
代わりに使用しました。)
前述のように、トラバーサルはEither
s だけでなく、すべてのモナド計算に適用されます。State
したがって、ステートフルな計算 (scalaz によってモデル化された) の順序付け、非決定論的計算 (List
モナド)の順序付けなど、幅広い問題に同じアプローチを使用できます。