The basic Elm example that I wish I'd had
Tags: elm, beginners
Published: 2018-07-20 22:35:46

---
title: The basic Elm example that I wish I'd had
published: true
description: When originally exploring Elm, I fumbled my way through figuring out how to manage state in an Elm application, and managed to make my life much more difficult than it needed to be. I hope this helps someone avoid the pain I put myself through!
tags: [elm, beginners]
---

_Note: You don't need very much Elm knowledge at all to benefit from this, but equally this isn't really intended for anyone with zero knowledge of Elm. It's more for people who have just started playing around with the absolute basics of the language and are trying to understand how to approach "communication between components" (or rather, why not to think that way!)_

_Update on October 9th, 2018: updated for Elm 0.19_


Today I was re-creating an example application in Elm (the original example was in React+Redux), and realised that I'd written the Elm example that I wish I'd had myself when I first started exploring the language, so I thought it might be worth sharing with a wider audience. 

### Initial setup

If you don't have Elm installed on your machine already, you might want to take a look at the [installation instructions](https://guide.elm-lang.org/install.html) now. 

With Elm installed, in a terminal at a location of your choosing, run `elm init` to initialise your new project. This will create an `elm.json` file for you, along with a `src` directory. _Note that this doesn't create the app directory for you, so make sure that you're inside the directory you actually want your application to live in at this point._

Start by creating a `Main.elm` file inside of your `src` directory, and open it in whichever editor you prefer (although if you want a recommendation, Visual Studio Code along with the [Elm language plugin](https://marketplace.visualstudio.com/items?itemName=sbrink.elm) is super nice to work with), and add your module declaration to the top: 

```elm
module Main exposing (..)
```

### A quick intro to the Elm Architecture

The key to an Elm application is the Elm Architecture, which - at least in an application that doesn't talk to the outside world at all - looks like this: 

![](https://thepracticaldev.s3.amazonaws.com/i/pfdd18lau3fmjhv63bou.png)

Which means we need a `Model`, a `View`, and an `Update`. But what are these? 

#### Model

Our `Model` is where we're going to define the `state` of our application. The entire idea behind this example is to consider the state of our application before anything else, although we'll talk about this in a little more detail later. 

#### View 

Our `view` is going to be a function that takes a `Model`, and returns a representation of our [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction) in Elm. Elm includes a virtual DOM, along with an HTML library to enable you to write your markup whilst taking advantage of the guarantees that Elm gives you.

#### Update

Our `update` function is going to be responsible for handling any updates to our `Model`. It's important to note that despite what the name implies, no update to the model actually happens in situ - instead, `update` will return a new model, which will be passed to our `view` function, allowing any necessary changes to be rendered in our application.

### What we're going to build

Okay, with that out of the way, let's have a look at our task. We're going to be building a high-tech, state-of-the-art, virtual room. That room is going to contain a single door, and an alarm system. The specifications for this are: 

```
If the alarm is armed, then it should be triggered by the door opening.
If the alarm has been triggered, then it can be disarmed, but not armed.
If the door is open, the alarm's current state can not be altered manually.
If the door is open it can be closed.
If the door is closed it can be opened or locked.
If the door is locked it can be unlocked.
```

So we know that we need to have a room that contains two things, each of which can be represented in a number of possible states, we know what combination of states can possibly be represented, and we know what the transitions are between the possible states. To my mind, we're looking at: 


```
-- POSSIBLE STATES:

Door: 
  Locked
  Closed
  Opened

Alarm:
  Armed
  Disarmed
  Triggered

Combined: 
  Locked + Armed
  Locked + Triggered
  Locked + Disarmed
  Unlocked + Armed
  Unlocked + Triggered
  Unlocked + Disarmed
  Opened + Triggered
  Opened + Disarmed

-- POSSIBLE TRANSITIONS: 

Door: 
  Closed <-> Locked
  Closed <-> Opened

Alarm: 
  Armed -> Triggered
  Triggered -> Disarmed
  Armed <-> Disarmed
```

But what about the entire application? In a perfect world, it appears that it can only really exist in one state: the state in which the room is being displayed. But for good measure let's consider the idea that we may introduce some logic error at some point, and if we do that, we might want to have some sort of hint as to what happened. So let's consider this application to have two overall state possibilities: `DisplayingRoom`, which needs to be aware of both the `Door` and the `Alarm`, and `Failure`, which needs to have some sort of message to tell us how it got there. 

Well, it feels like we've just described our `Model`, on top of our possible door and alarm states. Let's add all of that into our `Main.elm` file now: 

```elm
type Model 
  = DisplayingRoom DoorState AlarmState
  | Failure String

type DoorState
  = Opened
  | Closed
  | Locked

type AlarmState
  = Armed
  | Disarmed
  | Triggered
```

This is how you create a `custom type` in Elm, and all of the above are examples of custom types. 

### Coding our `update` function

At this stage, since we did all of our planning up-front, we also have all of the information that we need in order to code our `update` function. Update needs to take two things: a `message` (which is a description of the transition that needs to happen), and the `model` (which is the model _before_ the update is applied), and it will return a new model. 

Our transitions then, will take the form of `messages`, and again thanks to our up-front thinking about our problem, we already know what they're going to be, so let's add them into our `Main.elm` file now: 

```elm
type Msg
    = Open
    | Close
    | Lock
    | Unlock
    | Arm
    | Disarm
```

(If you're wondering why we don't have a `Trigger` message, it's because we don't have any way in our specification to manually trigger the alarm on its own!)

It's weird that we haven't _seen_ anything yet though huh? Well, at first it is. In my experience sorting out your data before you sort out how things look works pretty nicely. 

Right, back to work! Our `update` function is going to take a `message` and a `model`, and it's going to return a new `model`. We're going to start by checking what the state of the application (i.e. the value of the `Model`) is:

```elm
update msg model =
  case model of
    DisplayingRoom doorState alarmState ->
      model
    Failure errorMessage -> 
      model
```

If we're in a Failure state, the `update` function isn't going to have any way to recover, so it can just return the `model`, and that's us done with `Failure` in our `update` function. 

So what do we know about our possible state combinations when we're displaying our room? We know that we have the most constraints when our door is open - in fact, we only have one possible transition from that state, since we can't interact with our alarm until the door is closed. Let's add that in now: 

```elm
update : Msg -> Model -> Model
update msg model =
    case model of
        DisplayingRoom doorState alarmState ->
          case doorState of
            Opened ->
              case msg of
                Close ->
                  DisplayingRoom Closed alarmState

                _ ->
                  Failure "unexpected message received while door was in Opened state"

        Failure _ -> 
          model
```

Now we're handling our `Close` message when the door is in the `Opened` state, and we're saying that in that scenario, the model we want to return is `DisplayingRoom Closed alarmState` - `alarmState` being the name of the variable that is holding the state that the alarm was in when it was passed into the model; this ensures that it is passed through to the new model without being altered when the door goes from `Opened` to `Closed`. 

We're also saying that for _any other message_ that is received when the door is in this state, we want to put our application into our `Failure` state, with a message saying how it got there. We will be coding the view in such a way that no other messages should be possible here, but at least now if we make a mistake, we'll be have an easy way to give ourselves that information. 

Next, let's handle our `Closed` door state. In this case, we know that if the door is closed, and we receive an `Open` message, then we need to care about the state of the alarm, and we also know that when the door is closed it's possible for us to manually change the state of the alarm too. So let's add all of that in: 

```elm
                Closed ->
                  case msg of
                    Open ->
                      case alarmState of
                        Armed ->
                          DisplayingRoom Opened Triggered

                        _ ->
                          DisplayingRoom Opened alarmState

                    Lock ->
                      DisplayingRoom Locked alarmState

                    Arm ->
                      DisplayingRoom Closed Armed

                    Disarm ->
                      DisplayingRoom Closed Disarmed

                    _ ->
                      Failure "unexpected message received while door was in Closed state"
```

Okay, so now if our door is opened whilst our alarm is armed, it will trigger the alarm. And we're handling the messages associated with the alarm now too. So all that's left to add is any messages that are possible whilst our door is `locked`: 

```elm
                Locked ->
                  case msg of
                    Unlock ->
                      DisplayingRoom Closed alarmState

                    Arm ->
                      DisplayingRoom Locked Armed

                    Disarm ->
                      DisplayingRoom Locked Disarmed

                    _ ->
                      Failure "unexpected message received while door was in Locked state"
```

That's it for our `update` functionality! So now we've added in everything that describes how our data can be represented, and all the ways that can change, it's time to add some `view` code so we can see if it all works. 

### Coding our `view` function

As I mentioned earlier, there's an `HTML` library for Elm which is what allows us to write HTML within our Elm code. Import that now by adding ```import Html exposing (..)``` beneath your module declaration.

Let's start with the simplest part; our `failure` function. We know we get a message from the model in this state, so let's create a function to display it: 

```elm
failure message =
    div []
        [ p [] [ text message ] ]
```

Next, let's create our `Door`. We know it needs to have two possible messages when it's closed, but only one message if it's either opened or locked. It would be easy to make a single function to represent all of these different states based on what's passed in, but frankly it's not a lot of code to create them all separately, and who knows how much further they'll diverge as my high-tech door-and-alarm application expands - let's not prematurely optimise ourselves into a corner. Have a look at my door module [on Github](https://github.com/dnimmo/elm-state-machine-example/blob/master/src/View/Door.elm), and you'll see we have all the door functionality that we need in order to meet our acceptance criteria here. 

Now we're going to do the same thing for our `Alarm`, which you can also view [on Github](https://github.com/dnimmo/elm-state-machine-example/blob/master/src/View/Alarm.elm). You'll note that in this case, we're saying that Alarm needs to know not only the `message` that it has the ability to send (remember our alarm only ever has the ability to send one message depending on what state our application is in - it can never be in a state where we can `arm` and `disarm` it at the same time), but also it needs to know if any action is allowed. This gives us the ability to disable any alarm messages from being sent, but crucially the `Alarm` module itself _doesn't need to receive any messages from anywhere to make this happen_. We'll handle that when we wire together our `view` function in `Main.elm`, which we'll do right now.

Remember to `import` your `Door` and `Alarm` modules into your `Main.elm` too. If you have them set up the same way as I do (i.e. `View/Door.elm` and `View/Alarm.elm`, and with each of those exposing a separate function depending on the door/alarm state), you can import those as follows: 

```elm
import View.Alarm as Alarm exposing (armedAlarm, disarmedAlarm, triggeredAlarm)
import View.Door as Door exposing (closedDoor, lockedDoor, openDoor)
```

We want to display our `door` and our `alarm`, and we want to make sure they're displayed in the correct state depending on the state of our application. We know now what state our application is in to begin with, and what states it can possibly be in thereafter, and we've built the modules that will allow us to display what we need, so we just need to add the bit in the middle! Our `view` function in `Main.elm` then, should look like this: 

```elm
view : Model -> Html Msg
view model =
    case model of
        Failure message ->
            failure message

        DisplayingRoom doorState alarmState ->
            div
                []
                [ div
                    [ class "doorPanel" ]
                    [ case doorState of
                        Opened ->
                            openDoor Close

                        Closed ->
                            closedDoor Open Lock

                        Locked ->
                            lockedDoor Unlock
                    ]
                , div
                    [ class "alarmPanel " ]
                    [ case alarmState of
                        Armed ->
                            armedAlarm Disarm (doorState /= Opened)

                        Disarmed ->
                            disarmedAlarm Arm (doorState /= Opened)

                        Triggered ->
                            triggeredAlarm Disarm (doorState /= Opened)
                    ]
                ]
```

Here you can see we're checking the `doorState` that came out of our `model`, and calling the appropriate function depending on the state of the door, along with the appropriate message(s). We're doing the same thing with `alarmState`, and into each of our alarm functions, we're passing the correct message to match up to our desired behaviour within the function, and we're also passing in the result of a check on `doorState /= Opened`, since we told our `alarm` functions to expect a boolean to state whether any action is allowed or not. 

### Wiring up the runtime

We need Elm's core `Browser` module in order to make this do anything in the runtime. Add `import Browser` to the top of your `Main.elm` file, below your module declaration. (Note: Ordinarily you'd need to `install` new packages before they could be imported, but `Browser` was installed automatically when you ran `elm init`)

`Browser` has a few functions, but the one that we need here is [Browser.sandbox](https://package.elm-lang.org/packages/elm/browser/latest/Browser#sandbox). Sandbox allows you to create an application that uses the Elm architecture, but that doesn't talk to the "outside world" (i.e. any external APIs or JavaScript). It needs to take a `record` that has three fields: `init`, `update`, and `view`. We already have `update` and `view` functions, but the `init` is going to describe the application's initial model - let's say that we're going to be displaying the room, with the door closed, and the alarm armed: 

```elm
initialModel : Model
initialModel = DisplayingRoom Closed Armed

main : Program () Model Msg
main =
    Browser.sandbox
        { init = initialModel 
        , view = view
        , update = update
        }
```

### Running our application

Okay, so that's everything we need to do - in your terminal, run `elm make src/Main.elm`. This will give you an `index.html` file, which you can open in your browser of choice.

### It just...worked?

It feels like there should be more to do really, doesn't it? It can be a bit odd at first, if you're used to web development being a cycle of "see how it looks -> play around with the code -> see how it looks now", but I've found working from the `model` downwards rather than the `view` upwards makes a _lot_ of sense, and keeps things nice and clean.

I hope this is useful to someone!