完全な実施例のためにいくつかの簡単な定義:
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
type User = String
type Location = String
type Address = String
case class UserProfile(user: User, location: Location, addresses: Address)
def getUserById(id: Long): Future[Option[User]] = id match {
case 1 => Future.successful(Some("Foo McBar"))
case _ => Future.successful(None)
}
def getLocationById(id: Long): Future[Option[Location]] = id match {
case 1 => Future.successful(Some("The Moon"))
case _ => Future.successful(None)
}
def getAddressById(id: Long): Future[Option[Address]] = id match {
case 1 => Future.successful(Some("123 Moon St."))
case _ => Future.successful(None)
}
そして、完全を期すために、ここでScalazフリー実装は次のようになります:
def getProfile(uid: Long, lid: Long, aid: Long): Future[Option[UserProfile]] =
for {
maybeUser <- getUserById(uid)
maybeLocation <- getLocationById(lid)
maybeAddress <- getAddressById(aid)
} yield (
for {
user <- maybeUser
location <- maybeLocation
address <- maybeAddress
} yield UserProfile(user, location, address)
)
すなわち、 map
を入れ替える必要があるのと同じように、私たちはfor-comprehensionsを入れ子にしなければなりません。 Int
の値はFuture[Option[Int]]
の内部にある可能性があります。
ScalazまたはCatsのOptionT
モナドトランスは、この入れ子なしでFuture[Option[A]]
のようなタイプで作業できるように設計されています。
import scalaz.OptionT, scalaz.std.scalaFuture._
def getProfile(uid: Long, lid: Long, aid: Long): OptionT[Future, UserProfile] =
for {
user <- OptionT(getUserById(uid))
location <- OptionT(getLocationById(lid))
address <- OptionT(getAddressById(aid))
} yield UserProfile(user, location, address)
それともFuture[Option[UserProfile]]
を望んでいた場合、あなただけのrun
を呼び出すことができます:その後、
def getProfile(uid: Long, lid: Long, aid: Long): Future[Option[UserProfile]] = (
for {
user <- OptionT(getUserById(uid))
location <- OptionT(getLocationById(lid))
address <- OptionT(getAddressById(aid))
} yield UserProfile(user, location, address)
).run
そして:
scala> getProfile(1L, 1L, 1L).foreach(println)
Some(UserProfile(Foo McBar,The Moon,123 Moon St.))
中間結果のいずれかがある場合たとえば、あなたはこれを書くことができますNone
、全体がNone
:
scala> getProfile(1L, 1L, 0L).foreach(println)
None
scala> getProfile(0L, 0L, 0L).foreach(println)
None
もちろん、いずれかの要求が失敗した場合、最初のエラーですべてが失敗します。脚注として
、リクエストが互いに依存していない場合は、あなたがそれらapplicatively代わりのmonadically構成することができます。より正確に
import scalaz.Scalaz._
def getProfile(uid: Long, lid: Long, aid: Long): Future[Option[UserProfile]] = (
OptionT(getUserById(uid)) |@|
OptionT(getLocationById(lid)) |@|
OptionT(getAddressById(aid))
)(UserProfile.apply _).run
このモデルの計算をし、それが実行できるため、より効率的かもしれリクエストを並行して処理します。
これは明示的に 'OptionT'に関するものなので、' scalaz'タグ(そしておそらくは 'monad-transformers'や' scala-cats')を持つべきでしょうか? –