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])

+3


source to share


3 answers


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)))

      

+3


source


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

      

0


source


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).

0


source







All Articles