Question

I'm trying to figure out what the appropriate way to deal with this problem is in a functional way. I'm assuming there's a functional data structure or technique that will make this easy to handle.

The problem is that I'm making an API call to get two bits of data, I use one in another API call and if that call works then I use the other bit of data in a second API call. I'm having trouble getting that all to flow together. The API calls can fail of course, so they return Either, and I'd like it to be all one long chain, but I can't figure out how to do that.

In an imperative language I'd do this:

let response = apiCall1();
// error handling, return failure

// do some processing on the response
let json = getJsonBody(response);
let bit1 = json['bit1'];
let bit2 = json['bit2'];

let response2 = apiCall2(bit1);
// error handling, return failure

let response3 = apiCall3(bit2);
// error handling, return failure

return response3 // return success

If the result from each API call was the input to the next function I'd do something like this:

return apiCall1()
   .map(jsonBody)
   .map(apiCall2)
   .map(jsonBody)
   .map(apiCall3)

So what's the functional way to accomplish this?

apiCall1 ----
   |         |
   |         |
   v         |
apiCall2     |
   |         |
   |         |
   v         |
apiCall3 <---|
Était-ce utile?

La solution

This is where the monadic properties of Eithers come in handy (although you don't have to understand monads to take advantage of them). Most functional programming languages have a way to easily write this sort of chaining. In Haskell it's do notation. In Scala it's for comprehensions. Since I'm more familiar with Scala, I'll demonstrate that below.

val response = for {
  response  <- apiCall1().right
  json = getJsonBody(response)
  response2 <- apiCall2(json("bit1")).right
  response3 <- apiCall3(json("bit2")).right
} yield response3

Here, you use the right-projection of each of the Eithers. It basically performs a flatMap on the Right values of the results of the api calls. As soon as it hits a Left however, it will short-circuit the remainder of the calls and yield the Left as the final result.

Note this is just syntactic sugar. The compiler translates it to a series of flatMaps, and you can write it that way if you prefer, but this way is much easier to read.

Licencié sous: CC-BY-SA avec attribution
scroll top