错误值flatMap不是带有Seri​​alizable的产品的成员,用于Future [Option]

时间:2017-09-07 15:17:23

标签: scala

以下代码块无法构建并出现错误:

value flatMap is not a member of Product with Serializable
[error]         if (matchingUser.isDefined) {

以下是代码:

for {
  matchingUser <- userDao.findOneByEmail(email)
  user <- {
    if (matchingUser.isDefined) {
      matchingUser.map(u => {
        // update u with new values...
        userDao.save(u)
        u
      })
    } else {
      val newUser = new User(email)
      userDao.create(newUser)
      newUser
    }
  }
} yield user

方法userDao.findOneByEmail(email) returns an未来[选项[用户]] object. My Google searches are only about withand左'类型。

也许我没有以正确的方式这样做,请教我如何正确地做到这一点。

2 个答案:

答案 0 :(得分:1)

if语句的第一个分支返回Option[User],另一个返回User。因此,推断整个语句的结果具有类型Product with Serializable,因为它是两者中唯一常见的超类型。

您可以将if中的最后一个语句打包成Option(只做Option(newUser)而不是newUser),或者更好的是,使用fold而不是整个if(matchingUser.isDefined) {...}东西:

 matchingUser.fold { 
   val u = new User(email)
   userDao.create(u)
   u
 } { u => 
   userDao.save(u)
   u
 }

这将使该语句的结果为Option[User],如您所希望的那样......但它仍然无法编译。 问题在于你不能在for-comprehension中混合使用不同类型的monad:因为第一个是Future,所以其他所有monad也必须如此。你不能在那里Option

如何解决这个问题?好吧,一种可能性是让userDao.createuserDao.save返回他们刚刚保存的对象的未来。也就是说,一般来说可能是更好的事情,那么你拥有的东西,因为现在你在用户实际存储之前返回...如果create操作后来失败怎么办?然后你可以像这样重写你的理解:

for {
  matchingUser <- userDao.findOneByEmail(email)
  user <- matchingUser.fold(userDao.create(new User(email)))(userDao.save)
} yield user

或者完全摆脱它(对于像这样的简单案例来说,理解是一种矫枉过正的行为):

  userDao
   .findOneByEmail(email)
   .flatMap(_.fold(usrDao.create(new User(email)))(userDao.save))

或者,在这种情况下,使用模式匹配而不是fold可能看起来更好一些:

 userDao
   .findOneByEmail(email)
   .flatMap { 
      case Some(u) => userDao.save(u)
      case None => userDao.create(new User(email))
    }

答案 1 :(得分:0)

以下是我使用解决方案重现您的问题的测试示例。 基本上你的object T { import scala.concurrent.Future import scala.concurrent.ExecutionContext.Implicits.global def future1: Future[Option[Int]] = ??? def future2(i: Int): Future[Double] = ??? for { matchingUser <- future1 user <- { if (matchingUser.isDefined) { matchingUser.map { i => future2(i) } } else { Some(future2(42)) } } match { case None => Future.successful(-42.0) case Some(x) => x } } yield user } 返回Future(Future of Future)的包装器,但它应该是Future,作为for-understanding的第一个声明。 这就是为什么我已经应用了一些额外展开的原因。见下面的示例。

注意:它看起来不太好,我更喜欢用flatMap地图重写它。

  val userFuture = future1.flatMap { 
      case Some(i) => future2(i)
      case None => future2(42)
  }

同样用flatMap实现:

@Output() addCode = new EventEmitter<string[]>();
...

onAdd(code) {
 this.addSelectedCode.push(code);
 this.addCode.emit(this.addSelectedCode);
}