对于带有if过滤器的表达式会导致错误

时间:2016-08-06 20:57:25

标签: scala

如何重构我的代码,以便在表达式中没有if语句?

我收到执行异常:

[NoSuchElementException: Future.filter predicate is not satisfied]

我正在做一个if选项,定义如下,导致问题:

val maybeUserFut: Future[Option[User]] = 
    for {
          usernameOpt <- lookupUsername("...")
          if usernameOpt.isDefined
            userOpt <- getUser(usernameOpt.get)
        } userOpt

我应该开始使用哪种模式来避免此错误?

1 个答案:

答案 0 :(得分:0)

for {
  usernameOpt <- lookupUsername("...")
  if usernameOpt.isDefined
    userOpt <- getUser(usernameOpt.get)
} yield userOpt

转化为

lookupUsername("...")
  .withFilter({ usernameOpt => usernameOpt.isDefined })
  .flatMap({ usernameOpt =>
    getUser(usernameOpt.get).map({ userOpt =>
      userOpt
    })
})

Future.withFilter定义为(source):

def filter(p: T => Boolean)(implicit executor: ExecutionContext): Future[T] =
  map {
    r => if (p(r)) r else throw new NoSuchElementException("Future.filter predicate is not satisfied")
  }

所以没有简单的转换可以发挥作用。

我可能会写一些类似的东西:

lookupUsername("...").flatMap({ usernameOpt =>
  usernameOpt match {
    case None           => Future.successful(None)
    case Some(username) => getUser(username)
  }
})

如果你想使用for理解语法,你需要一个像Scalaz OptionT这样的monad变换器,它将显式提升抽象为Future(例如case None => Future.successful(None)):

import scalaz._
import Scalaz._
import scalaz.OptionT._

val maybeUserFut: Future[Option[User]] = 
  (for {
    username <- optionT(lookupUsername("..."))
    user <- optionT(getUser(username))
  } yield user).run