---
title: Booleans and Enums
teaser: Refactor common Boolean smells using enums (union types).
tags: web,elm,good code
author: Joël Quenneville
published_on: 2017-09-20
---

**Booleans** are one of the first data types new programmers learn and with good
reason: having only two states makes them one of the simplest. Surely something
so simple can't be abused?

It turns out it's easier than you'd think to make a mess with Booleans.
Languages with first-class support for **enums** like [C], [Swift], [Rust], and
[TypeScript] provide the tools for some really nice refactorings.

Note that enums go by a few alternate names such as **sum types** and **union
types**. They're also a subset of a greater concept called **[algebraic data types
(ADT)]** so you may hear them referred to as this.

The refactorings below can be applied in any language. I'll be using [Elm] in
the examples for its first-class support of enums (which it refers to as **union
types**) and strict, friendly compiler.

[TypeScript]: https://www.typescriptlang.org/docs/handbook/enums.html
[Swift]: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Enumerations.html
[Rust]: https://rustbyexample.com/custom_types/enum.html
[C]: https://msdn.microsoft.com/en-us/library/whbyts4t.aspx
[algebraic data types (ADT)]: http://blog.jenkster.com/2016/06/functional-mumbo-jumbo-adts.html
[Elm]: https://guide.elm-lang.org/types/union_types.html

## Three-state Boolean

Booleans represent one of two states: `True` or `False`. If we allow a Boolean
value to be `null` (or in Elm's case `Maybe`), then there are three possible
states. This is the [three-state Boolean problem].

Let's say we're building a checkout system that allows restaurants to take
orders online. When we start there are two types of orders: delivery or pickup.
Two states, that sounds like a Boolean! But what about the case when the user
hasn't selected an option yet? Sounds like the classic use of a Maybe. But now,
our option has _three_ states, not two.

```elm
type alias Order =
  { id : Int
  , delivery : Maybe Bool
  }
```

With three states, we've outgrown our Boolean. Instead of using the primitive
`Maybe Bool`, we can construct a union type that represents our three states:

```elm
type DeliveryType = Delivery | Pickup | NoneSelected

type alias Order =
  { id : Int
  , deliveryType : DeliveryType
  }
```

This approach has a few advantages:

* **Extensible** it's easy to add a fourth state.
* **Readable** it's more obvious what a delivery of type `NoneSelected` is than
  a delivery of type `Nothing`.

[three-state boolean problem]: https://thoughtbot.com/blog/avoid-the-threestate-boolean-problem

## Double Boolean

Another common situation is to have two dependent Booleans. Let's say our
checkout system allows orders to be fulfilled via third-party delivery services
such as DoorDash. An order is either fulfilled via a third party or it isn't.
Sounds like another Boolean!

```elm
type alias Order =
  { id : Int
  , delivery : Bool
  , thirdParty : Bool
  }
```

Take a closer look and you realize that the two Booleans are dependent. What we
now have is a single state that is expressed as a combination of two Boolean
flags. We have a clunky system for expressing 4 states. Even worse, some of the
states don't even make sense.

What is a non-delivery order fulfilled via third party???

```elm
{ id = 1
, delivery = False
, thirdParty = True
}
```

We can express our four states using a union type instead:

```elm
type DeliveryType
  = Delivery
  | Pickup
  | ThirdPartyDelivery
  | NotSelected

type alias Order =
  { id : Int
  , deliveryType : DeliveryType
  }
```

As previously, this has a few advantages:

* **Extensible** it's easy to add a fifth state.
* **Readable** calculating the state based on dependant Booleans is clunky.
* **Correct** no invalid states permitted.

## Boolean Flags

You're building a role-playing game as a side project because games are fun! You
want players to interact with non-player characters (NPC). Not a problem. We
already have a `Character` type. We just need to set the correct attributes on
it.

The implementation is a single line but what does it even mean? Those are
probably flags of some sort but who knows what they do. There's got to be a
better way.

```elm
npc : Character
npc =
  Character True True False
```

Turns out there is! Using union types, it's pretty obvious what the attributes
of this NPC are.

```elm
npc : Character
npc =
  Character Immortal Civilian Stationary
```

It is completely OK to define a union type with only two values.

```elm
type Mortality = Mortal | Immortal
type MilitaryStatus = Civilian | CityGuard
type Mobility = Stationary | Mobile

type alias Character =
  { mortality : Mortality
  , militaryStatus : MilitaryStatus
  , mobility : Mobility
  }
```

While it may feel like you're just re-implementing Booleans, this approach has a
few advantages:

* **Extensible** it's easy to add a third state.
* **Readable** this technique adds a massive readability boost to your code.
* **Correct** it is now impossible to accidentally pass the character's mobility
  to a function when you really meant to pass mortality. The compiler now knows
  the difference between the two and can catch the error.

## Why not strings

After seeing all this, you maybe thinking "Union types are cool and all but
couldn't I just use strings?". There is a small difference between the two that
makes a big impact.

Consider the following type:

```elm
type Mobility = Mobile | Stationary
```

How many different allowed values are there? _Two._

Now consider the equivalent string.

```elm
type alias Mobility = String
```

How many allowed values are there? _Infinite._

This is a big deal. If you're writing a `case` statement for a mobility, you
will need to handle all those infinite other invalid values you don't care about
with a catch-all. If you ever add a new mobility (say "Flying"), it will just
silently be ignored by your case statement.

```elm
move : Int -> Character -> Character
move distance character =
  case character.mobility of
    "Mobile" ->
      { character | position = character.position + distance }

    "Stationary" ->
      character
    _ ->
      character
```

On the other hand, adding a new `Flying` value to the union type results in a
compiler error that tells you all the places you need to change to accommodate
the new mobility. Yay type safety!

<pre>
<samp>-- MISSING PATTERNS ------------------------------------------------------------

This `case` does not have branches for all possibilities.

11|>    case character.mobility of
12|>      Mobile ->
13|>        { character | position = character.position + distance }
14|>
15|>      Stationary ->
16|>        character

You need to account for the following values:

    Flying</samp>
</pre>

Because there are infinite strings, any string is an allowed mobility including
typos. The compiler can't help you here.

```elm
npc : Character
npc =
  Character "Mobil" 55
```

On the other hand, because there are only a limited set of values in the union
type, the compiler can easily catch typos and even suggest fixes:

<pre>
<samp>-- NAMING ERROR ----------------------------------------------------------------

Cannot find variable `Mobil`

20|   Character Mobil 55
                ^^^^^
Maybe you want one of the following?

    Mobile</samp>
</pre>

## Primitive obsession

Booleans are some of the first data structures we are introduced to when
learning to program. It seems so sensible to model values that have two-states
as Booleans. This often leads to trouble because most "two-state" values in the
real world aren't as binary as you might assume.

_Once you have two states, you will likely be adding a third or more at some
point even if just an "empty" or "null" state._ Unless you are dealing with
states of truth, Booleans are probably not the best way to model your problem.

Instead of using a [primitive], build your own union type to express your states.
Not only does this better allow you to model your domain, but it also improves
readability and adds better type safety. Win all round!

[primitive]: http://wiki.c2.com/?PrimitiveObsession
