---
title: Elm's universal pattern
teaser: Want to build a random generator? A JSON decoder? Combine multiple Maybes
  into a more complex one? Do it all with this one simple trick.
tags: elm,web
author: Joël Quenneville
published_on: 2017-01-13
---

In Elm, there are a lot of data structures that wrap other values. It's very
common to want to combine values inside these wrappers. It turns out that
there's a universal pattern in Elm to do this.

## The setup

We have a user with a name, age, and address. The address is wrapped in its own
type. Finally, our `Model` is a list of users.

```elm
type Address = Address String

type alias User =
  { name : String
  , age : Int
  , address : Address
  }
```

To build a `User` from scratch, we could combine the various pieces like this:

```elm
user : User
user =
  User "Bob" 42 (Address "123 Main Street")
```

You could visualize this as:

![Tree of the pieces that make up User](https://images.thoughtbot.com/jq-elm-univeral-pattern/TtUdEXMRV64yIFfEi2Z9_user-tree-simple.png)

What if the various parts are wrapped in some type of container?

## Maybe

[`Maybe`] allows us to tag values possibly not having a value. To wrap a value
in a `Maybe`, you can use the `Just` constructor:

[`Maybe`]: https://package.elm-lang.org/packages/elm/core/latest/Maybe

```elm
maybeName : Maybe String
maybeName =
  Just "Bob"

maybeAge : Maybe Int
maybeAge =
  Just 42
```

If we got the raw string for the address as `Maybe String`, how can we wrap it
with our `Address` constructor while keeping it inside the `Maybe`? Enter
[`Maybe.map`]:

[`Maybe.map`]: https://package.elm-lang.org/packages/elm/core/latest/Maybe#map

```elm
rawAddress : Maybe String
rawAddress =
  Just "123 Main Street"

maybeAddress : Maybe Address
maybeAddress =
  Maybe.map Address rawAddress
```

Note that if `rawAddress` were `Nothing` then `maybeAddress` would be `Nothing`
too.

What about combining our three `Maybe` values together to get a `Maybe User` ?
Because the constructor `User` has three arguments and we are combining three
`Maybe`s, we can use [`Maybe.map3`]

[`Maybe.map3`]: https://package.elm-lang.org/packages/elm/core/latest/Maybe#map3

```elm
maybeUser : Maybe User
maybeUser =
  Maybe.map3 User maybeName maybeAge maybeAddress
```

Once again, note that if any of `maybeName`, `maybeAge`, or `maybeAddress` is
`Nothing` then `maybeUser` will also return `Nothing`.

The whole structure could be visualized as:

![Tree of various Maybe components of a Maybe User](https://images.thoughtbot.com/jq-elm-univeral-pattern/2qzOS5xGS4mdPmeAsK2t_user-tree-maybe.png)

## List

You are probably very familiar with how lists work. You can create them using
the `[]` literal syntax.

```elm
nameList : List String
nameList =
 ["Alice", "Bob", "Carol"]

ageList : List Int
ageList =
  [25, 42, 64]
```

What about wrapping our `Address` type around a list of raw addresses? You
probably correctly guessed we should use [`List.map`].

[`List.map`]: https://package.elm-lang.org/packages/elm/core/latest/List#map

```elm
rawAddresses : List String
rawAddresses =
  ["42 Some Plaza", "123 Main Street", "789 Central Avenue"]

addressList : List Address
addressList =
  List.map Address rawAddresses
```

Now how do we combine these three lists to get a list of users? If you said
[`List.map3`], you're right!

[`List.map3`]: https://package.elm-lang.org/packages/elm/core/latest/List#map3

```elm
userList : List User
userList =
  List.map3 User nameList ageList addressList
```

Combining multiple lists in this way, especially two lists into a list of
tuples, is sometimes referred to as _zipping_.

The whole thing could be visualized as:

![Tree of various List component of List User](https://images.thoughtbot.com/jq-elm-univeral-pattern/OB5I7ZrS3eBo2l79rfDg_user-list-tree.png)

You're probably starting to see a pattern here. Let's try something more
difficult.

## JSON decoders

Unlike the other wrapper types we've looked at so far, [JSON decoders] don't wrap
values in the traditional sense. Instead, they encode the relationship between a
JSON structure and your Elm types.

[JSON decoders]: https://package.elm-lang.org/packages/elm/json/latest/Json-Decode

Lets say we have the following JSON:

```json
{ name: "Alice",
  age: "42"
  address: "123 Main Street"
}
```

You can tell Elm what type a value at a given key is using [`Decode.field`] and
giving it a decoder. `Json.Decode` module provides decoders for the basic
datatypes. For our name and age fields, it might look like:

[`Decode.field`]: https://package.elm-lang.org/packages/elm/json/latest/Json-Decode#field

```elm
nameDecoder : Decoder String
nameDecoder =
  Decode.field "name" Decode.string

ageDecoder : Decoder Int
ageDecoder =
  Decode.field "age" Decode.int
```

What if you want to wrap a decoded string with `Address`? There's [`Decode.map`]
for that.

[`Decode.map`]: https://package.elm-lang.org/packages/elm/json/latest/Json-Decode#map

```elm
rawStreetDecoder : Decoder String
rawStreetDecoder =
  Decode.field "address" Decode.string

addressDecoder : Decoder Address
addressDecoder =
  Decode.map Address rawStreetDecoder
```

We can combine all three decoders to decode a user with [`Decode.map3`]:

[`Decode.map3`]: https://package.elm-lang.org/packages/elm/json/latest/Json-Decode#map3

```elm
userDecoder : Decoder User
userDecoder =
  Decode.map3 User nameDecoder ageDecoder addressDecoder
```

You can visualize this as:

![Tree of json decoders](https://images.thoughtbot.com/jq-elm-univeral-pattern/4h24GSQYT5mO3uCsPkKR_user-decoder-tree.png)

## Random generators

Like JSON decoders, [random generators] don't wrap values in the traditional
sense. Instead, they wrap the idea of a value that _will be randomly generated
in the future_. This makes them a little bit harder to reason about but all the
same rules apply as with the simpler data structures.

[random generators]: https://package.elm-lang.org/packages/elm/random/latest/

For simple values we can use the built-in generators:

```elm
ageGenerator : Generator Int
ageGenerator =
  Random.int 1 100 -- random number between 1 and 100
```

For the name, we want to pick from a particular list of strings.  We can  use
the [`Random.uniform`] function to do that. It picks an item randomly from a
list with equal probability.

```elm
nameGenerator : Generator String
nameGenerator =
  Random.uniform "Alice" ["Bob", "Carole"]
```

We can also use [`Random.map`] to wrap a random address string with `Address`:

```elm
rawStreetGenerator : Generator String
rawStreetGenerator =
  Random.uniform "42 Some Plaza" ["123 Main Street", "789 Central Avenue"]

addressGenerator : Generator Address
addressGenerator =
  Random.map Address rawStreetGenerator
```

Finally how can we combine all these generators to get a single generator of
users?

```elm
userGenerator : Generator User
userGenerator =
  Random.map3 User nameGenerator ageGenerator addressGenerator
```

The combined generator can be visualized as:

![Tree of random generators](https://images.thoughtbot.com/jq-elm-univeral-pattern/tuYDCI6Sf2WLZza6bwHH_user-generator-tree.png)

[`Random.uniform`]: https://package.elm-lang.org/packages/elm/random/latest/Random#uniform
[`Random.map`]: https://package.elm-lang.org/packages/elm/random/latest/Random#map

## Comparing side by side

You may have noticed a pattern as we were going through these four examples.
Let's look some of those functions side by side:

### Building an `Address`

```elm
Maybe.map  Address rawAddress
List.map   Address rawAddresses
Decode.map Address rawStreetDecoder
Random.map Address rawStreetGenerator
```

### Building a `User`

```elm
Maybe.map3  User maybeName     maybeAge     maybeAddress
List.map3   User nameList      ageList      addressList
Decode.map3 User nameDecoder   ageDecoder   addressDecoder
Random.map3 User nameGenerator ageGenerator addressGenerator
```

## General Concepts

You're probably seeing a pattern now. Here are some general tips for doing this
type of work:

* When building complex structures, start with the smallest sub-part of your
  structure (generally a primitive value) and then combine them with each other
  to form more complex structures.
* You can keep transforming and combining those combined structures as much as
  you want to create arbitrarily complex structures.
* Use `map` to transform or wrap a value inside a wrapper structure.
* Use `map2`, `map3`, and so on to combine multiple wrapped values together
* If you get into a situation where mapping gives you nested containers
  (e.g. `Maybe (Maybe Address)`), you'll want to take a look at the `andThen`
  function for your container.
