递归返回 Future.Right 的串联

  • 本文关键字:Right 返回 Future 递归 scala
  • 更新时间 :
  • 英文 :


在下面的递归recHelper方法中,如果所有元素都返回Right则返回未来Left,我需要返回一个期货列表。问题是我无法连接结果。如何使此代码工作?

def either1 (i:Int): Future[Either[String,Int]] = Future {
                    if (i<3)
                       Right(i*2)
                    else
                       Left("error 1")
}
def either2 (i:Int): Future[Either[String,Int]] = Future {
                    if (i<3)
                       Right(i*2)
                    else
                       Left("error 2")
}

val seq = Seq (1,1,2,2)

def recHelper(remaining: List[Int]): Future[Either[String, Seq[Int]]] = {
    remaining match {
      case Nil => Nil
      case h :: t => (if (h % 2 == 0) either1(h) else either2(h)).map {
        headEither =>
            headEither match {
              case Left(s) => Future { Left(s) }
              case Right(n) => Future { n :: recHelper(t) :: Nil } /// ERROR
            }
      }
    }
  }
  recHelper(seq.toList)

Nil => Nil的情况表明您应该再次阅读FutureEither monads 的作用:Nil被推断为类型 List[Int] 。它缺少unit的两个应用,即:

  1. 你忘了把它包成Right让它Either[List[Int]]
  2. 你忘了把它包成Future,让它Future[Either[List[Int]]

这同样适用于其他情况。这是一个有效的版本:

import scala.concurrent._
import scala.util._
import scala.concurrent.ExecutionContext.Implicits.global
def either1(i: Int): Future[Either[String, Int]] = Future {
  if (i<3) Right(i*2)
  else Left("error 1")
}
def either2(i: Int): Future[Either[String, Int]] = Future {
  if (i<3) Right(i*2)
  else Left("error 2")
}
val seq = Seq(1, 1, 2, 2)
def recHelper(remaining: List[Int]): Future[Either[String, List[Int]]] = {
  remaining match {
    case Nil => Future { Right(Nil) }
    case h :: t => for {
      hEith <- (if (h % 2 == 0) either1(h) else either2(h))
      res <- (hEith match {
        case Left(s) => Future { Left(s) }
        case Right(n) => for {
          tEith <- recHelper(t)
        } yield tEith.map(n :: _)
      })
    } yield res
  }
}
recHelper(seq.toList)

你不能偶然地用两个堆叠的单子来构建一个复杂的嵌套理解。我只能再次强烈建议看看斯卡拉猫和EitherT。他们构建 monad 转换器库不仅仅是为了好玩:同时处理两个堆叠的 monad 实际上是非常痛苦的。

另一种解决方案:

def recHelper(remaining :Seq[Int]
             ,acc       :Seq[Int] = Seq()
             ) :Future[Either[String, Seq[Int]]] = remaining match {
    case Seq()  => Future(Right(acc))
    case h +: t => (if (h % 2 == 0) either1(h) else either2(h)).flatMap {
        case Left(s) => Future(Left(s))
        case Right(n) => recHelper(t, n +: acc)
    }
}
recHelper(seq)
//res0: Future[Either[String,Seq[Int]]] = Future(Right(List(4, 4, 2, 2)))

最新更新