Looking for simple yet practical examples of using monads in Elm

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 :smile:

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.

8 Likes