3

我正在使用安全的社會和reactivemongo庫和scala編寫一個play 2.3應用程序。 現在我試圖實現UserService [T]特質,但我得到updatePasswordInfo方法的編譯錯誤。 這是方法:編譯未來的[選項[BasicProfile]]方法中的錯誤

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 { 

有什麼不對?

回答

1

如果你真的想做查找快速失敗(儘管它不是那麼有用),然後從數據庫重新加載更新的用戶,像這樣的事情應該不需要使用scalaz:

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) 
    val newPassword = Json.obj("passswordInfo" -> info) 
    //update the document 
    for { 
     userO <- UserServiceLogin.find(query).one[BasicProfile] //fail fast (not sure this is really useful) 
     updatedUser<-UserServiceLogin.update(query, newPassword).map(_=>userO).recover{case _ =>None} 
     actualUser <- UserServiceLogin.find(query).one[BasicProfile] 
    } yield actualUser 

    } 
2

如果映射在Future[A]你會得到一個Future[B],其中T爲你傳遞給map拉姆達返回的類型結束。

由於該lambda返回Future[B]在這種情況下,您最終將與Future[Future[B]],它不符合預期的類型。

簡單的解決方法是使用flatMap,這需要從AFuture[B]的lambda。


此外,你返回一個Option[LoginUser]但該方法將返回一個Option[BasicProfile]。編譯器推斷出一個公共的超類型,在這種情況下它是Product with Serializable,因爲它們都是case類。

概括起來的使用,而不是

scala.concurrent.Future[Product with Serializable] 
^_____________________^^_________________________^ 
      1      2 
  1. flatMapmap
  2. 返回一個BasicProfile代替LoginUser,或改變該方法返回類型Future[Option[LoginUser]]

通過方式,有一個很多改進的空間,因爲你可以使用for-comprehension和scalaz的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)) 
+0

LoginUser是BasicProfile類的子類型。 編譯器給我一個錯誤:他說沒有找到optionT – 2014-09-24 12:13:58

+1

@albertoadami,那麼這是因爲你需要添加ScalaZ到你的依賴關係。無論如何,在這種情況下,你可以很容易做到,因爲你不需要對'user'對象做任何事情。 – 2014-09-24 13:17:26

1

有幾種方法,使你的代碼可以得到改善。

例如,在觸發查詢之前,您不需要查找用戶。

此外,檢查您的查詢是否實際成功(如果API允許)會很好。

第三,我不確定LoginUser對應BasicProfile的方式。您的代碼似乎沒有進行任何類型的轉換。 如果 LoginUser是BasicProfile的一個子類,或可以以某種方式轉換爲BasicProfile,你可以嘗試這樣的事:

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 
         ) 
    UserServiceLogin.update(query, newPassword) //update the document 
    for { 
     user <- UserServiceLogin.find(query).one 
    } yield user.map(_.asInstanceOf[BasicProfile]) //return the new LoginUser 

    } 
+0

我不認爲找到用戶是無用的:如果不存在,快速失敗可能是一個理想的屬性。 – 2014-09-24 11:44:40

+0

這裏幾乎沒有任何收益,因爲我們試圖找到用戶,如果我們之前沒有檢查,我們可能會保存一個數據庫請求。當然只是IMO。 – Ashalynd 2014-09-24 11:57:58

+0

LoginUser是BasicProfile – 2014-09-24 12:17:04