An Introduction to Elm

by Evan Czaplicki

Contents

1. Introduction

Elm is a functional language that compiles to JavaScript. It competes with projects like React as a tool for creating websites and web apps. Elm has a very strong emphasis on simplicity, ease-of-use, and quality tooling.

This guide will:

By the end I hope you will not only be able to create great web apps in Elm, but also understand the core ideas and patterns that make Elm nice to use.

If you are on the fence, I can safely guarantee that if you give Elm a shot and actually make a project in it, you will end up writing better JavaScript and React code. The ideas transfer pretty easily!

A Quick Sample

Of course I think Elm is good, so look for yourself.

Here is a simple counter. If you look at the code, it just lets you increment and decrement the counter:

import Html exposing (Html, button, div, text)
import Html.Events exposing (onClick)

main =
  Html.beginnerProgram { model = 0, view = view, update = update }

type Msg = Increment | Decrement

update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1

view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (toString model) ]
    , button [ onClick Increment ] [ text "+" ]
    ]

Notice that the update and view are entirely decoupled. You describe your HTML in a declarative way and Elm takes care of messing with the DOM.

Why a functional language?

Forget what you have heard about functional programming. Fancy words, weird ideas, bad tooling. Barf. Elm is about:

No combination of JS libraries can ever give you this, yet it is all free and easy in Elm. Now these nice things are only possible because Elm builds upon 40+ years of work on typed functional languages. So Elm is a functional language because the practical benefits are worth the couple hours you’ll spend reading this guide.

I have put a huge emphasis on making Elm easy to learn and use, so all I ask is that you give Elm a shot and see what you think. I hope you will be pleasantly surprised!

2. Install

Note: If you do not want to install yet, you can follow along in this guide with the online editor and the online REPL.

After installing through any of those routes, you will have the following command line tools:

We will go over how they all work in more detail right after we get your editor set up!

Troubleshooting: The fastest way to learn anything is to talk with other people in the Elm community. We are friendly and happy to help! So if you get stuck during installation or encounter something weird, visit the Elm Slack and ask about it. In fact, if you run into something confusing at any point while learning or using Elm, come ask us about it. You can save yourself hours. Just do it!

Configure Your Editor

Using Elm is way nicer when you have a code editor to help you out. There are Elm plugins for at least the following editors:

If you do not have an editor at all, Sublime Text is a great one to get started with!

You may also want to try out elm-format which makes your code pretty!

The Command Line Tools

So we installed Elm, and it gave us elm-repl, elm-reactor, elm-make, and elm-package. But what do they all do exactly?

elm-repl

elm-repl lets you play with simple Elm expressions.

$ elm-repl
---- elm-repl 0.18.0 -----------------------------------------------------------
 :help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
> 1 / 2
0.5 : Float
> List.length [1,2,3,4]
4 : Int
> String.reverse "stressed"
"desserts" : String
> :exit
$

We will be using elm-repl in the upcoming “Core Language” section, and you can read more about how it works here.

Note: elm-repl works by compiling code to JavaScript, so make sure you have Node.js installed. We use that to evaluate code.

elm-reactor

elm-reactor helps you build Elm projects without messing with the command-line too much. You just run it at the root of your project, like this:

git clone https://github.com/evancz/elm-architecture-tutorial.git
cd elm-architecture-tutorial
elm-reactor

This starts a server at http://localhost:8000. You can navigate to any Elm file and see what it looks like. Try to check out examples/01-button.elm.

Notable flags:

elm-make

elm-make builds Elm projects. It can compile Elm code to HTML or JavaScript. It is the most general way to compile Elm code, so if your project becomes too advanced for elm-reactor, you will want to start using elm-make directly.

Say you want to compile Main.elm to an HTML file named main.html. You would run this command:

elm-make Main.elm --output=main.html

Notable flags:

elm-package

elm-package downloads and publishes packages from our package catalog. As community members solve problems in a nice way, they share their code in the package catalog for anyone to use!

Say you want to use elm-lang/http and NoRedInk/elm-decode-pipeline to make HTTP requests to a server and turn the resulting JSON into Elm values. You would say:

elm-package install elm-lang/http
elm-package install NoRedInk/elm-decode-pipeline

This will add the dependencies to your elm-package.json file that describes your project. (Or create it if you do not have one yet!) More information about all this here!

Notable commands:

3. Core Language

This section will walk you through Elm’s simple core language.

This works best when you follow along, so after installing, start up elm-repl in the terminal. (Or use the online REPL.) Either way, you should see something like this:

---- elm repl 0.18.0 -----------------------------------------------------------
 :help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
>

The REPL prints out the type of every result, but we will leave the type annotations off in this tutorial for the sake of introducing concepts gradually.

We will cover values, functions, lists, tuples, and records. These building blocks all correspond pretty closely with structures in languages like JavaScript, Python, and Java.

Values

Let’s get started with some strings:

> "hello"
"hello"

> "hello" ++ "world"
"helloworld"

> "hello" ++ " world"
"hello world"

Elm uses the (++) operator to put strings together. Notice that both strings are preserved exactly as is when they are put together so when we combine "hello" and "world" the result has no spaces.

Math looks normal too:

> 2 + 3 * 4
14

> (2 + 3) * 4
20

Unlike JavaScript, Elm makes a distinction between integers and floating point numbers. Just like Python 3, there is both floating point division (/) and integer division (//).

> 9 / 2
4.5

> 9 // 2
4

Functions

Let’s start by writing a function isNegative that takes in some number and checks if it is less than zero. The result will be True or False.

> isNegative n = n < 0
<function>

> isNegative 4
False

> isNegative -7
True

> isNegative (-3 * -4)
False

Notice that function application looks different than in languages like JavaScript and Python and Java. Instead of wrapping all arguments in parentheses and separating them with commas, we use spaces to apply the function. So (add(3,4)) becomes (add 3 4) which ends up avoiding a bunch of parens and commas as things get bigger. Ultimately, this looks much cleaner once you get used to it! The elm-html package is a good example of how this keeps things feeling light.

If Expressions

When you want to have conditional behavior in Elm, you use an if-expression.

> if True then "hello" else "world"
"hello"

> if False then "hello" else "world"
"world"

The keywords if then else are used to separate the conditional and the two branches so we do not need any parentheses or curly braces.

Elm does not have a notion of “truthiness” so numbers and strings and lists cannot be used as boolean values. If we try it out, Elm will tell us that we need to work with a real boolean value.

Now let’s make a function that tells us if a number is over 9000.

> over9000 powerLevel = \
|   if powerLevel > 9000 then "It's over 9000!!!" else "meh"
<function>

> over9000 42
"meh"

> over9000 100000
"It's over 9000!!!"

Using a backslash in the REPL lets us split things on to multiple lines. We use this in the definition of over9000 above. Furthermore, it is best practice to always bring the body of a function down a line. It makes things a lot more uniform and easy to read, so you want to do this with all the functions and values you define in normal code.

Note: Make sure that you add a whitespace before the second line of the function. Elm has a “syntactically significant whitespace” meaning that indentation is a part of its syntax.

Lists

Lists are one of the most common data structures in Elm. They hold a sequence of related things, similar to arrays in JavaScript.

Lists can hold many values. Those values must all have the same type. Here are a few examples that use functions from the List library:

> names = [ "Alice", "Bob", "Chuck" ]
["Alice","Bob","Chuck"]

> List.isEmpty names
False

> List.length names
3

> List.reverse names
["Chuck","Bob","Alice"]

> numbers = [1,4,3,2]
[1,4,3,2]

> List.sort numbers
[1,2,3,4]

> double n = n * 2
<function>

> List.map double numbers
[2,8,6,4]

Again, all elements of the list must have the same type.

Tuples

Tuples are another useful data structure. A tuple can hold a fixed number of values, and each value can have any type. A common use is if you need to return more than one value from a function. The following function gets a name and gives a message for the user:

> import String

> goodName name = \
|   if String.length name <= 20 then \
|     (True, "name accepted!") \
|   else \
|     (False, "name was too long; please limit it to 20 characters")

> goodName "Tom"
(True, "name accepted!")

This can be quite handy, but when things start becoming more complicated, it is often best to use records instead of tuples.

Records

A record is a set of key-value pairs, similar to objects in JavaScript or Python. You will find that they are extremely common and useful in Elm! Let’s see some basic examples.

> point = { x = 3, y = 4 }
{ x = 3, y = 4 }

> point.x
3

> bill = { name = "Gates", age = 57 }
{ age = 57, name = "Gates" }

> bill.name
"Gates"

So we can create records using curly braces and access fields using a dot. Elm also has a version of record access that works like a function. By starting the variable with a dot, you are saying please access the field with the following name. This means that .name is a function that gets the name field of the record.

> .name bill
"Gates"

> List.map .name [bill,bill,bill]
["Gates","Gates","Gates"]

When it comes to making functions with records, you can do some pattern matching to make things a bit lighter.

> under70 {age} = age < 70
<function>

> under70 bill
True

> under70 { species = "Triceratops", age = 68000000 }
False

So we can pass any record in as long as it has an age field that holds a number.

It is often useful to update the values in a record.

> { bill | name = "Nye" }
{ age = 57, name = "Nye" }

> { bill | age = 22 }
{ age = 22, name = "Gates" }

It is important to notice that we do not make destructive updates. When we update some fields of bill we actually create a new record rather than overwriting the existing one. Elm makes this efficient by sharing as much content as possible. If you update one of ten fields, the new record will share the nine unchanged values.

Comparing Records and Objects

Records in Elm are similar to objects in JavaScript, but there are some crucial differences. The major differences are that with records:

Elm encourages a strict separation of data and logic, and the ability to say this is primarily used to break this separation. This is a systemic problem in Object Oriented languages that Elm is purposely avoiding.

Records also support structural typing which means records in Elm can be used in any situation as long as the necessary fields exist. This gives us flexibility without compromising reliability.

4. The Elm Architecture

Contents

The Elm Architecture is a simple pattern for architecting webapps. It is great for modularity, code reuse, and testing. Ultimately, it makes it easy to create complex web apps that stay healthy as you refactor and add features.

This architecture seems to emerge naturally in Elm. We first observed it in the games the Elm community was making. Then in web apps like TodoMVC and dreamwriter too. Now we see it running in production at companies like NoRedInk and Pivotal. The architecture seems to be a consequence of the design of Elm itself, so it will happen to you whether you know about it or not. This has proven to be really nice for onboarding new developers. Their code just turns out well-architected. It is kind of spooky.

So The Elm Architecture is easy in Elm, but it is useful in any front-end project. In fact, projects like Redux have been inspired by The Elm Architecture, so you may have already seen derivatives of this pattern. Point is, even if you ultimately cannot use Elm at work yet, you will get a lot out of using Elm and internalizing this pattern.

The Basic Pattern

The logic of every Elm program will break up into three cleanly separated parts:

This pattern is so reliable that I always start with the following skeleton and fill in details for my particular case.

import Html exposing (..)


-- MODEL

type alias Model = { ... }


-- UPDATE

type Msg = Reset | ...

update : Msg -> Model -> Model
update msg model =
  case msg of
    Reset -> ...
    ...


-- VIEW

view : Model -> Html Msg
view model =
  ...

That is really the essence of The Elm Architecture! We will proceed by filling in this skeleton with increasingly interesting logic.

Fork me on GitHub