Scala用于理解Future,List和Option

时间:2015-10-05 19:27:35

标签: scala playframework future reactivemongo

我正在Scala和Play Framework中构建一个被动站点,我的数据模型经常需要编写FutureOption,并构建Future {{1}来自先前值的/ List来获取我需要的结果。

我写了一个带有假数据源的简单应用程序,你可以复制和粘贴它应该编译。我的问题是,如何在我的案例Set中以可使用的形式返回结果。目前,我正在回复UserContext

我想在纯Scala中更好地学习语言,所以我现在正在避免使用Scalaz。虽然我知道我最终应该使用它。

Future[Option[Future[UserContext]]]

2 个答案:

答案 0 :(得分:6)

您创建了FutureO,它结合了FutureOption的效果(如果您正在研究Scalaz,则会与OptionT[Future, ?]进行比较)。

记住for ... yield类似于FutureO.map,结果类型将始终为FutureO[?](如果您Future[Option[?]],则为result.future

问题是您想要返回Future[UserContex]而不是Future[Option[UserContext]]。基本上你想要松开Option上下文,所以如果用户存在与否,你需要明确处理。

在这种情况下,可能的解决办法可能是遗漏FutureO,因为您只使用过一次。

case class NoUserFoundException(id: Long) extends Exception 

// for comprehension with Future
val result = for {
  user <- UserDB.findById(userId = 111) flatMap (
            // handle Option (Future[Option[User]] => Future[User])
            _.map(user => Future.successful(user))
             .getOrElse(Future.failed(NoUserFoundException(111)))
          )
  players <- Future.traverse(user.player_ids)(x => PlayerDB.findById(x))
  teams  <- Future.traverse(user.team_ids)(x => TeamDB.findById(x))
} yield UserContext(user, teams.flatten, players.flatten)
// result: scala.concurrent.Future[UserContext]

如果你有多个函数返回Future[Option[?]],你可能 喜欢使用FutureO,在这种情况下你可以创建一个额外的函数Future[A] => FutureO[A],所以你可以在同一for理解中使用你的函数(全部在FutureO monad中):

def liftFO[A](fut: Future[A]) = FutureO(fut.map(Some(_)))

// for comprehension with FutureO
val futureO = for {
  user <- FutureO(UserDB.findById(userId = 111))
  players <- liftFO(Future.traverse(user.player_ids)(x => PlayerDB.findById(x)))
  teams  <- liftFO(Future.traverse(user.team_ids)(x => TeamDB.findById(x)))
} yield UserContext(user, teams.flatten, players.flatten)
// futureO: FutureO[UserContext]

val result = futureO.future flatMap (
   // handle Option (Future[Option[UserContext]] => Future[UserContext])
   _.map(user => Future.successful(user))
    .getOrElse(Future.failed(new RuntimeException("Could not find UserContext")))
)
// result: scala.concurrent.Future[UserContext]

但正如您所看到的,您将始终需要处理&#34;选项上下文&#34;在您返回Future[UserContext]之前。

答案 1 :(得分:2)

为了扩展Peter Neyens的答案,我经常会把一堆monad - &gt; monad转换在一个特殊的隐式类中,并根据需要导入它们。这里我们有两个monad,Option[T]Future[T]。在这种情况下,您将None视为失败的Future。你可能会这样做:

package foo {
    class OptionOps[T](in: Option[T]) {
        def toFuture: Future[T] = in match {
            case Some(t) => Future.successful(t)
            case None => Future.failed(new Exception("option was none"))
        }
    }
    implicit def optionOps[T](in: Option[T]) = new OptionOps[T](in)
}

然后您只需导入import foo.optionOps

然后:

val a: Future[Any] = ...
val b: Option[Any] = Some("hi")
for {
    aFuture <- a
    bFuture <- b.toFuture
} yield bFuture // yields a successful future containing "hi"