2017-09-07 2 views
2

次のコードブロックがエラーでビルドに失敗します。エラー値[オプション]

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左型。

私はこれを適切な方法で行っていないかもしれません。適切にこれを行う方法を教えてください。

答えて

0

if文の最初の分岐はOption[User]を返し、他の一つはUserを返します。したがって、ステートメント全体の結果は、そのタイプの唯一の一般的なスーパータイプであるため、タイプがProduct with Serializableであると推定されます。

あなたはにif内部の最後のステートメントをラップすることができOptionか、いっそのこと、全体if(matchingUser.isDefined) {...}ブツの代わりにfoldを使用する(だけではなく、newUserOption(newUser)が行う):

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

これは結果を行いますあなたはおそらく意図したようにその文のOption[User]になる...しかし、それはまだコンパイルされません。 問題は、for-comprehensionに異なる種類のモナドを混在させることができないことです。最初のものがFutureだったので、他のものもすべて同じでなければなりません。そこにOptionを持つことはできません。

どうすれば回避できますか?さて、1つの可能性は、userDao.createuserDao.saveを保存したばかりのオブジェクトの未来を返すことです。つまり、実際に格納される前にユーザーを返すので、一般的にはもっと良いことですが、次に持っているのはおそらく... create操作が失敗したらどうなりますか?そして、あなたはこのようなあなたのために、理解を書き換えることができます:それは、

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

または:

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

をそれとも完全にそれを取り除く(ため-理解このような単純なケースで過剰です)この場合には代わりにfoldのパターンマッチングと少し立派に見えることがあります。

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

あなたは永続/更新メソッドで正しいですが、Future [Unit]の代わりに永続/更新されたレコードを返すべきです。私のコードが投稿されたものと同じくらいシンプルであれば、残念ですが、現実には私のコードを簡単に単純化して投稿しました。折り畳み方法についての良い点も、私はあなたの答えまでそれを知らなかった。私はすべてのリファクタリングが完了したらすぐにあなたの答えを受け入れます。ありがとう! – Jeep87c

0

解決策を使用して問題を再現するためのテスト例です。 基本的には、あなたのuserは、Future(Option of Future)の周りにラッパーを返しますが、それは、理解の最初のステートメントとしてFutureであると予想されます。 そのため、私はいくつかの追加アンラッピングを適用しました。以下のサンプルを参照してください。

注:これはうまく見えません。私はflatMapマップで書き直すことをお勧めします。

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 
} 

そしてflatMapで実装同じ:

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

あなたはまた、部分関数の構文を使用することができます( 'future1.flatMap {ケースいくつかの(I)=> ...')直接一致するのではなく –

関連する問題