Practical Examples
In day-to-day Elm, I most commonly use andThen
in the following scenarios:
- Cleaning up logic involving
Maybe
values getting returned along the way in a chain of functions - Decoding JSON differently based on the value of a given field (e.g. “role”)
- Chaining multiple HTTP requests such that only the final result come through my
update
- Less frequently, generating dependent rolls in a random generator
Chaining functions that return Maybe
@pdamoc already gave an example of chaining Maybes with Maybe.andThen
. It’s a great way to avoid having to do a bunch of extra presence-checks. It’s very common to use Maybe.andThen
in combination with List.head
.
Here’s another, somewhat more complex example. In a social networking application, you want to find where a user’s most popular friend lived the longest. The problem is that everything there could be not-present: The user passed into the function is optional, even if they are present they may not have any friends, and even if they have a most popular friend the friend may not have any residences in the system.
It might sound like all this uncertainty would leave you drowning in nested case
statements but Maybe.andThen
can save you here. Example from this article on problem solving with Maybe.
maybeMostPopularFriendLongestResidence : Maybe User -> Maybe Address
maybeMostPopularFriendLongestResidence maybeUser =
maybeUser
|> Maybe.andThen mostPopularFriend
|> Maybe.andThen longestResidence
Conditionally decoding based on a field
You have this user type:
type User = Admin String | RegularUser String
you get user data from the server that you want to decode into one of either Admin
or RegularUser
based on the the value of the “role” field in the JSON. So first you decode the role field, then based on what value was there you choose which decoder to use. Example from this article on 5 common JSON decoding situations.
userDecoder : Decoder User
userDecoder =
Json.Decode.field "role" Json.Decode.string
|> Json.Decode.andThen (\role ->
case string of
"regular" -> regularUserDecoder
"admin" -> adminDecoder
_ -> JD.fail ("Invalid user role: " ++ role)
)
Chaining dependent random rolls
First role a random gender, then based on that result, run either the appropriate gendered name generator. This example comes from my talk Rolling Random Romans where I try to build a generator for historically accurate ancient Roman names
nameGenerator : Generator String
nameGenerator =
genderGenerator
|> Random.andThen (\gender ->
case gender of
Female -> femaleNameGenerator
Male -> maleNameGenerator
)
Chaining HTTP requests
First make an HTTP request to get the current user’s server ID, then use that ID to make another HTTP request to get the user’s details.
getUser : Task Http.Error User
getUser =
Http.toTask getCurrentUserId
|> Task.andThen (\userId -> Http.toTask (getUserDetails userId))
For some fancier chaining of HTTP requests, check out the solution this discourse thread which uses recursion + Task.andThen
to clean up code to fetch data over a paginated API.