Chaining futures and options idiomatically
def foo(user, id): Future[Option[Bar]] =
bar(user, id).map(_.map(_.address.flatMap(_.street)))
.flatMap {
case Some(street) =>
baz(user, street).flatMap(_ => get(id))
case None => successful(None)
}
The function bar returns Option[UserInfo]
, which is then mapped to UserInfo
. Address
also is Option
, so I flatMap to be able to access street
. Then simply, if there is a street that I want to call baz
, if not, then None
. Ignore the business logic, this is for example.
There's a problem with the code here as it won't compile.
Some(street)
is Option
because flatMap
line 3 is called as a result of displaying on the first _
instead of _.address
.
While I could get this to work with some kind of parenthesis, etc., this code starts to read and reason hard.
Is there an answer for understanding?
PS: This example may be missing any type information, so please ask and I'll clarify.
EDIT:
case class UserInfo(id: UserId, address: Option[Address])
case class Address(street: Option[List[Street]], state: Option[State])
source to share
If I understood the method signatures correctly:
def bar(user, id): Option[UserInfo]
def baz(user, List[Street]): Future[BarId]
def get(id): Option[Bar]
You can implement your method like this:
val streetsOpt: Option[List[Street]] = bar(user, id).flatMap(_.flatMap(_.address.flatMap(_.street)))
streetsOpt.flatMap(streets => {
baz(user, streets).map(_ => get(id))
}).getOrElse(successful(None)))
source to share
Just taking a quick look at it, on this line:
baz(user, street).flatMap(_ => get(id))
I don't think the last flatMap won't work as expected because you seem to be passing a function that is of type something like:
f: => A
i.e. fetching the underlying value from some context while flatMap expects you to unpack that value, then wrap it in a new context and thus type:
f: A => M[B]
When you make a call
get(id)
Shouldn't this be applied to a map method that expects a function like:
f: A => B
source to share
There are several ways to deal with such nested contexts. I've talked about three that I know: monad transformers (explicit, more "standard" but a little more verbose), Kleisli (very elegant if you're ready to write in a no-glasses style), or my scalaz-transfigure library (a little immature, a little less explicit but very concise).
source to share