scala - Play 中 Future[Option[BasicProfile]] 方法的编译错误

标签 scala playframework-2.0 securesocial reactivemongo play-reactivemongo

我正在使用 Scala 使用安全的社交和 reactivemongo 库编写一个 play 2.3 应用程序。 现在我正在尝试实现 UserService[T] 特性,但我在 updatePasswordInfo 方法上遇到编译错误。 这是方法:

def updatePasswordInfo(user: LoginUser,info: PasswordInfo): scala.concurrent.Future[Option[BasicProfile]] = {
    implicit val passwordInfoFormat = Json.format[PasswordInfo]
    //the document query
    val query = Json.obj("providerId" -> user.providerId,
                         "userId" -> user.userId
                        )
    //search if the user exists
    val futureUser: Future[Option[LoginUser]] = UserServiceLogin.find(query).one
    futureUser map {
      case Some(x) => val newPassword = Json.obj("passswordInfo" -> info)// the new password
                      UserServiceLogin.update(query, newPassword) //update the document
                      val newDocument: Future[Option[LoginUser]] = UserServiceLogin.find(query).one
                      newDocument map {
                        case Some(x) => x
                        case None => None

                      } //return the new LoginUser
      case None => None
    }

  }

这是编译器错误:

/Users/alberto/git/recommendation-system/app/security/UserService.scala:203: type mismatch;
[error]  found   : scala.concurrent.Future[Product with Serializable]
[error]  required: Option[securesocial.core.BasicProfile]
[error]                       newDocument map {

怎么了?

最佳答案

如果您映射到 Future[A],您最终会得到一个 Future[B],其中 T 是类型从传递给 map 的 lambda 返回。

由于 lambda 返回一个 Future[B] 在这种情况下你最终得到一个 Future[Future[B]],这与预期不匹配类型。

简单的解决方法是使用 flatMap,它需要一个从 AFuture[B] 的 lambda。


此外,您要返回一个 Option[LoginUser],但该方法应该返回一个 Option[BasicProfile]。编译器推断出一个通用父类(super class)型,在本例中为 Product with Serializable,因为它们都是案例类。

总结一下

scala.concurrent.Future[Product with Serializable]
^_____________________^^_________________________^
          1                        2
  1. 使用flatMap代替map
  2. 返回 BasicProfile 而不是 LoginUser,或者将方法返回类型更改为 Future[Option[LoginUser]]

顺便说一下,还有很大的改进空间,因为您可以使用 for-comprehension 和来自 scalaz 的 OptionT monad 转换器来使整个事情更漂亮。

举个例子

import scalaz._; import Scalaz._

val newPassword = Json.obj("passswordInfo" -> info)
(for {
  // this is done only for failing fast in case the user doesn't exist
  _ <- optionT(UserServiceLogin.find(query).one)
  _ <- optionT(Future.successful(Some(UserServiceLogin.update(query, newPassword))))
  updatedUser <- optionT(UserServiceLogin.find(query).one)
} yield updatedUser).run

顺便说一下,这在 update 是一个同步调用的假设下工作,但可能(我希望)不是这种情况。如果它返回一个 Future[T] 只需将代码更改为

_ <- optionT(UserServiceLogin.update(query, newPassword).map(Some(_)))

或者如果它已经返回一个 Future[Option[T]]

_ <- optionT(UserServiceLogin.update(query, newPassword))

关于scala - Play 中 Future[Option[BasicProfile]] 方法的编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26014761/

相关文章:

json - 使用鉴别器编码 ADT 案例类,即使键入为案例类

java - Play 2 - 尚未定义默认的 EBean 服务器

playframework-2.0 - 使用 securesocial & play 2 查看自定义

javascript - 从 Scala.rx 0.3.2 更新到 0.4.0 时出现 LinkingErrors

scala - Scala 列表中的计数模式

java - 使用通用 Controller 时,如何返回特定 Controller 固有的 View ?

表单验证后的 Playframework IllegalStateException

java - 玩 SecureSocial 定制

playframework - 使用 Play Framework 自定义 SecureSocial 模块

Scala Recursive For Comprehension 仅在空列表前添加一次,为什么?