3

すべてが返される一連の操作の検証を実装したいと思いますEither[Error,Item] 。これは(最初の必要に応じて)フェイルファストである必要があります。つまり、Either [Error、Seq[Item]]を返します。エラーが発生した場合は、次の操作を実行したくないことは明らかです。しかし、将来的には、最初のエラーだけを返すのではなく、すべてのエラーを収集したいと思うかもしれません。

Scalazがその仕事をすることができることは知っていますが、今のところScalazのすべての部分を完全に理解しているわけではなく、Scalazを使用せずに、たとえば名前によるパラメーターを使用するより簡単な方法があると確信しています。

名前によるパラメータを順番に保存する方法はありますか?自分の操作を表す名前による値のシーケンスを作成できるようにするには?

つまり、ある種のタイプSeq[=> Either[Error,Item]] では、シーケンスの作成前にすべての操作を実行せずに、takeWhileやcollectFirstなどの何かを呼び出すことができますか?シーケンスを反復する場合にのみ操作が実行されることを期待します。

ありがとう

4

2 に答える 2

2

Seq[() => Either[Error, Item]]コレクションの作成時にa を使用して、計算を延期することができます。たとえば

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)

(ItemInt例の で、 はErrorですString)

次に、次の再帰関数を使用して、最初の失敗で停​​止して遅延処理できます。

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]](すべての操作を処理する)を生成したい場合。ちょっとした変更でそれを行うことができます:

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))
}

ご覧のとおり唯一の変更点は、パターン マッチの左のケースです。これを実行する:

scala> processAll(l)
1
2
3
4
5
res0: Either[Seq[Error],Seq[Item]] = Left(List(second error, error))

processAllfoldLeftジェネリックonに置き換えることができます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できますが、簡単ではありません。フォールドから早期に中止するのは難しいためです。これを行う必要がある場合は、他の可能なアプローチについての良い答えを次に示します。

于 2012-10-21T04:40:15.633 に答える
1

You should be able to pull this off with the type Seq[Function0[Either[Error, Item]]]. Function0 is, obviously, a zero-argument function. The rest should be self-explanatory.

Scalaz provides the type IO for exactly this purpose, so you could actually use that as well. You may not want to yet, however, if you're just beginning to work with Scalaz.

于 2012-10-21T02:29:02.410 に答える