---
title: What's Weird with Maybe List
teaser: When you spot `Maybe List` in your code, it may be time to think about alternatives.
tags: elm,good code,web
author: Joël Quenneville
published_on: 2018-05-21
---

When modeling data in a typed language like [Elm] or [Haskell], some
combinations of types are a bit strange. Take `Maybe (List a)`. It has _two_
empty states: `Nothing` and `Just []`. Does your code distinguish between these
two? Or do they mean the same thing?

[Elm]: http://elm-lang.org/
[Haskell]: https://www.haskell.org/

## Same thing

Lists already have an empty state: `[]`. If you only need to model "list of
values" and "no values" then you can rely on `List` alone.

In the following code, we see that `Nothing` and `Just []` are semantically the
same: they represent the absence of results.

```elm
view : Maybe (List Int) -> Html a
view calculation =
  case calculation of
    Nothing -> text "No Results"
    Just [] -> text "No Results"
    Just numbers -> viewResults numbers
```

Wrapping in `Maybe` doesn't add any extra information so we can simplify our
case statement by dropping the `Maybe` entirely.

```elm
view : List Int -> Html a
view calculation =
  case calculation of
    [] -> text "No Results"
    _ -> viewResults calculation
```

## Different things

Sometimes, `Nothing` and `Just []` are being used to represent _different_
states.

```elm
view : Maybe (List Int) -> Html a
view calculation =
  case calculation of
    Nothing -> text "In Progress"
    Just [] -> text "No Results"
    Just numbers -> viewResults numbers
```

A [custom union type] will do a better job at communicating the semantic
differences. Now you can see at a glance what possible states a calculation can
have.

```elm
type Calculation
  = InProgress
  | NoResults
  | Results (List Int)
```

[custom union type]: https://guide.elm-lang.org/types/union_types.html

## Getting fancy

Observant readers might have noticed that it's still possible to end up with the
weird state `Results []` using the union type defined above. Since we've
separated out the empty state from the list, we might want to get fancy and
guarantee a [non-empty list] for `Results`.

[non-empty list]: http://package.elm-lang.org/packages/mgold/elm-nonempty-list/3.1.0/List-Nonempty

```elm
type Calculation
  = InProgress
  | NoResults
  | Results (List.NonEmpty Int)
```

## Legitimate uses of `Maybe (List a)`

Does this mean that we should avoid `Maybe (List a)` at all cost? No. Consider
getting the head of a doubly-nested list:

```elm
[[1,2], [3,4]]
  |> List.head -- returns Just [1,2]
```

`Maybe (List a)` is a perfectly valid return type here. It represents a list
that may or may not be present. It does not carry any extra implicit semantic
meaning.

Returning an optional list often makes sense when querying another data
structure. Not only does it correctly model the uncertainty in the querying, it
also allows functions to be chained nicely with other `Maybe` helpers.

```elm
firstChild : Int -> MyTree a -> Maybe (MyTree a)
firstChild id myTree =
  myTree
    |> MyTree.childrenOfNode 10
    |> Maybe.andThen List.head
```

## Conclusion

The `Maybe (List a)` is a type that introduces ambiguity when data modeling.
Depending on what you're trying to represent, a simple `List a` or a custom union
type may better model your system. Don't discount it entirely though. It can
often make sense as the return type of a query function.
