If I understand your original problem correctly, it’s that those two (soon to be 3…) were painful to chain together. In particular, because the data returned from each task were not just intermediate results but instead were needed to construct the final value.
If you just wanted the evolution chain, the code would look nice:
request
|> Task.andThen getSpecie
|> Task.andThen getEvolutionChain
but you want all the intermediate values too. This is a fairly common problem. Here are some solutions people use
Nested andThen
The classic solution is to nest andThen
s:
request
|> Task.andThen (\pokemon ->
getSpecie pokemon
|> Task.andThen (\specie ->
getEvolutionChain specie
|> Task.andthen (\chain -> Task.succeed (pokemon, species, chain))
)
)
While this is nicer than having to map
at every step, all those lambdas can be a bit clunky.
Flipped andThen
We can flatten things and get rid of parens by reorganizing things a bit. If we flip andThen
's args, it will play nicer with the reverse pipe we want to use:
flippedAndThen : Task x a -> (a -> Task x b) -> Task x b
flippedAndThen value function =
Task.andThen function value
allTogether : Task Http.Error (Pokemon, Specie, EvolutionChain)
allTogether =
flippedAndThen request <| \pokemon ->
flippedAndThen (getSpecie pokemon) <| \specie ->
flippedAndThen (getEvolutionChain specie) <| \chain ->
Task.succeed (pokemon, specie, chain)
There is currently a proposal to change pipeline decoders to use this style. Interestingly, Haskell provides syntactic sugar called do notation that is built on this approach.
Helper Function
If you run into situations like this often, it may be helpful to define a helpers like:
chain3 : (a -> b -> c -> d) -> Task x a -> (a -> Task x b) -> (b -> Task x c) -> Task x d
chain3 function taskA taskB taskC =
taskA |> Task.andThen (\a ->
taskB a |> Task.andThen (\b ->
taskC b |> Task.andThen (\c -> Task.succeed <| function a b c)
)
)
This is just an abstraction of the nested andThen approach discussed earlier. If you use a it a lot it can be worth it since it gives you nicer invocations like:
chain3 (\a b c -> (a, b, c))
request
getSpecie
getEvolutionChain