Why Elm? (And How To Get Started With It)

Avatar of James Kolce
James Kolce on (Updated on )

If any discussion of the biggest problems in web development, there is no doubt complexity comes up. Between the use of dozens of tools, dependencies, and complexities inherent in the languages themselves, complexity is part of the job.

Modern web applications require new ways to think about how we handle processes. The problem of executing functions without a strict order or in unexpected moments — like the click of a button or an incoming network message — forces us to use different techniques, from callbacks and promises to more sophisticated tools like Reactive Extensions.

It is not surprising to have systems composed of several thousands of lines of code, and things can easily get out of control at that scale. Even the slightest of changes could have unintended consequences. Runtime errors are extremely common with JavaScript, and if we are not careful we can find ourselves trying to make impossible computations, like calling something that doesn’t exist or trying to process something that no longer is what you thought.

Article Series:

  1. Why Elm? And How To Get Started With It (You are here!)
  2. Introduction to The Elm Architecture and How to Build our First Application
  3. The Structure of an Elm Application

(Some of) The Advantages of Functional Programming

Controlled State

In a language like JavaScript, we can do almost whatever we want with the state of our programs. The lack of control with state is a common precursor to bugs. For example, trying to work with a value that has changed in unexpected ways.

Although state can’t disappear, it can be controlled; Functional languages seek to provide tools to work with it in more organized ways. Some of them, like Clojure, provide features like immutable data structures, but others go to the extreme and become pure functional languages, like Haskell, where every single detail about state has to be carefully thought.


Pure functions are a core concept of functional programming, and they are incredibly useful. We say that a function is pure when it always returns the same result given the same input and it doesn’t cause side-effects.

Because a function can’t do anything more than taking a value, processing that value, and returning the result — we can get more predictable programs.

Write What, Not How

Functional languages are declarative languages, it means that we can describe a process without having to worry about all the details of how the process is done. The consequences of having a clear view of the purpose of our code can be very positive because you can understand more easily what the program does without the implementation noise of how it is done.

Modularity and Composability

The principle of “Divide and conquer” is a common practice in functional programming. We split complex problems into several straightforward ones, creating a solution for each one and then combine them to solve the entire problem; By dividing our programs into small functions, we can increase reusability and maintainability.


Pure functional languages motivate programmers to think better about the programs they are building. From simple type annotations and mechanisms to control side-effects to complete architectures, like in Elm.

Although the initial development time can increase with such restrictions, the advantages in maintainability can compensate for the effort.

Elm: A Functional Programming Language for the web

Elm is a relatively new programming language to build web applications created by Evan Czaplicki in 2012 as part of his thesis about Concurrent FRP for Functional GUIs.

Because it’s targeted to the web—and browsers only understand a limited set of languages—the code that you write in Elm is compiled to HTML, CSS, and JS.

Immutable by design

All values on Elm are immutable, which means that once you have something, it will stay the same unless you create a new something as a copy.

The simplest advantage—because we don’t have to keep mutations in our head—reading code becomes a lot easier; when we see a value we can be sure it will stay the same. Also, you don’t have to worry about runtime errors related to unexpected values because of mutation.

Static Types

Static types are an excellent way to document systems. You can know quickly what a function does and its domain just by looking at the type definition. Type systems allow us to define exactly the kind of value that a function takes and returns.

But types are not only useful when they are read by developers, they also allow for better tools (like debuggers and testers) that increase the soundness of our code. Also, they prevent us from doing impossible computations, and by consequence, keeping us away from a lot of potential runtime errors.

Elm has an inferred type system. Which means that you don’t have to write types from the beginning and the compiler will try to infer the appropriate type for your functions, so you don’t get any productivity penalties like in other mainstream languages with types.

-- Function with its type annotation
square : Int -> Int
square n = n^2

-- This also works, because types are inferred
square n = n^2

Pure functions

Pure functions are the core concept of functional programming and produce a huge advantage building reliable and easy-to-maintain systems. Elm functions are pure and they receive inputs and produce new values as output, also, they always return the same result for each input without producing side-effects.


Asynchronous systems require a different approach to programming and Elm handles this by using Subscriptions. Because there are certain things that we don’t know when they are going to happen — like user interactions or network responses — we can subscribe to those events allowing us to act as soon as they occur without having to worry about implementation details.

Controlled side-effects

Pure functions are good, and we can try to have pure functions everywhere, but at one point or another, we have to handle interactions of our program with the external world, and we can do this in a controlled way using Commands.

Because input and output are also side-effects, if we use strictly pure functions everywhere, we won’t be able to do anything at all. Instead, we identify side-effects, and we make them explicit so that Elm can handle them in the right way for us.

Compatible with Your Existing Project

You don’t have to build or port a complete project to Elm — though that would be nice — you can include certain components made with Elm in an existing project.

Also, is possible to use existing Javascript inside your Elm applications, while keeping all the nice things and guarantees of the language.

Easy to Test

What do we get with static types? Awesome tools, including random test generators, where functions can be tested against random inputs depending on the domain of the function (defined by the types).

But probably one of the most exciting Elm features in this area is the fact that we can export the complete state of a program, and with it, we can do things like:

  • Send it to a library developer to reproduce a bug.
  • Load it in another browser or device to see how things behave.

And all this is done in a secure way, including network calls, thanks to the controlled side-effects using Commands.

A Better Architecture from the Beginning

When people started to build projects with Elm, they noticed that a pattern was emerging every time. That was the beginning of the Elm Architecture, the de facto way to build Elm applications.

Having a solid architecture to build our projects will assure us that we will have things under control even if our program increases in complexity. Also, we have the advantage that every time we take a new existing project, we won’t have a big problem understanding how the code is organized.

A sane package manager

Have you ever updated a JavaScript package and after starting your application you notice that something is not working as expected or is not working at all? Well, that’s an inexistent problem in Elm; the package manager automatically handles version numbers depending on the changes made, so you will never get breaking changes in a minor version anymore.

Beginner-friendly Compiler

We all know that compilers usually produce unreadable messages, but that is not the case with Elm. Every time you make a mistake, it will point out where the problem is, and an explanation of how to fix it.

Elm Compiler message

You can see more examples in the official Elm blog:

Setting Up our Development Environment

Reader Scott Phillips wrote in to say: “Elm 0.19 has breaking changes … but the thrust of the article still holds.” Our editorial advice would be to reference official documentation for installation and specific features, and enjoy this series as an introduction to why you might care to use Elm and think in an Elm-like way.

Installing the Elm Platform

Installing Elm is a quick process. After installing NodeJS, you can install Elm with NPM directly as other NodeJS packages:

npm install -g elm

Optionally, you can also install Elm using the installers for Mac or Windows. The links are available on the Install section in the An Introduction to Elm website.

Getting a Text Editor

Although Elm is a relatively new language, great tools to work with it already exist, especially for Atom using the Elmjutsu package.

But Atom is not the only one, the following editors are also supported:

You can pick the one you like the most. The installation process for those editors is outside of the scope of this article; I would recommend to check out their respective website.

Introduction to the Elm Platform

The Elm Platform: four tools that will be essential for the development of our Elm projects. A REPL to test small fragments of code. Reactor, a server that will allow us to see immediately our projects in the browser. Make, a tool to build our projects and the package manager that will provide us access to modules written by other people. Let’s take a look at these tools to see what we can do with them.

The Read–Eval–Print Loop

The Elm REPL is a tool to evaluate simple Elm code within a command line without having to create a file. As the name indicates, it first reads the input, then evaluates it and finally prints the result. This tool can be very useful to explore the language or to quickly execute a small piece of code.

To start using the REPL just open a terminal and execute the following command:


# Update: `elm repl` now

You should get something similar to this:

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

We can write Elm code next to the > symbol. As a simple test, let’s try to build a square function to get the result of multiplying a number by itself. We can do this by writing the following and pressing Enter.

square n = n * n

Now we have a square function available in the current environment that we can use in future evaluations, for example, try typing:

square 5

And it should return 25 : number. The first part is our result and the rest is the type of the result which is a number.

You can also write more complex code. Let’s write a simple factorial function:

factorial number = \
  if number == 0 then 1 \
  else number * factorial(number-1)

To write multiple lines you have to add a \ at the end of each one to tell the interpreter that you want to write more.

After typing the first line and pressing enter, a | character will appear, then you can write the next line of your code. Be careful with the indentation, it will return an error if you don’t add it; you have to add at least one space.

There are four commands that you can use directly when the REPL is active:

  • :help Will show all the available commands.
  • :flags You can modify options for the Elm compiler. Update: flags no longer exists.
  • :reset Restarts your current session without having to close and open the REPL again.
  • :exit Closes the REPL without having to close the command line window.

Elm Reactor

Elm-reactor is an interactive development tool that makes it easier to develop Elm programs. It will create a server that will compile your elm code on the fly, and you will be able to see the result directly on the browser.

Let’s see how we would use Reactor with a sample project. For this, you can clone the Elm Architecture Tutorial with Git and once you are in the project directory, execute the following command:


# Update: `elm reactor` now

A server will be created, and you will be able to access to it at http://localhost:8000 in your web browser.

You will see a navigation page that will contain the files in the directory of the project. Go to examples/01-button.elm and wait a moment while the file is compiled. Once if finished, you will be able to see a little widget to increase and reduce a number with two buttons.

That’s how we can see our project without producing the final files, very useful for the development process.

Two useful flags can be used with the elm-reactor command, to see the complete list you can run elm-reactor --help.

  • -a Sets a custom address for the server.
  • -p Sets a different port for the server (:8000 by default).

For example:

elm-reactor -a -p 3000

# Update: `elm reactor` now

Although Reactor is a very useful tool for the development of Elm applications, eventually you will get to the point where you will want to put your project in production, there is a tool for this job included in the Elm Platform: Make.

Elm Make

Once your project is ready for the world, you will have to translate the Elm code to something web browsers understand, HTML, CSS and JavaScript. We use a tool called elm-make for this task, it will take Elm files and will produce native files for the web.

For example, let’s try to compile the example we used in the previous section. Go to the examples directory in the Elm Architecture Tutorial project and execute the following command:

elm-make 01-button.elm --output=01-button.html

# Update: `elm make` now

When you execute this command, it will ask you to approve the installation of some packages, write Y and hit Enter, now let’s wait until the compilation is completed.

This will build our 01-button.elm file into a 01-button.html that can be open directly in the browser. If you go to the directory where 01-button.elm file is located, you will also find the new HTML file. You can open that file with your browser, and you will see the same result as in the previous section, but this time, we can see it without having to use any Elm tool; it’s plain HTML and Javascript.

Now we can distribute that file in the way we want, and any web browser will be able to open it.

The Elm Package Manager

When you start building non-trivial applications, you will soon find that writing everything by yourself is impractical. Usually, for the most common tasks, you will find packages already written by other people that you can use in your projects. For this, we use the Elm package manager, a tool to install and publish Elm packages.

As we saw in the previous section when we tried to compile our Elm file, the compiler asks us if we wanted to install the missing packages, that’s the job of the package manager in practice.

This tool is available as elm-package in your command line, and to install a package you can execute the following:

elm-package install 

# Update: package manager integrated into `elm` command now

Other commands include:

  • publish: Adds the package to the central catalog.
  • bump: Updates de version number of the package depending on changes.
  • diff: A utility to view the differences between package versions.

Elm Format

Elm Format is not part of the Elm Platform but it is an incredibly useful tool worth mentioning. It will format your source code according to the standard style guide, also you can set up your editor to do it automatically every time you save a .elm file.

You can check out Elm format on its Github repository.

Getting a Taste of Elm

Create a new folder somewhere on your computer and open it with your IDE or text editor. We will create a first-application.elm file, where we are going to include the following code:

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

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


type alias Model = Int

model : Model
model =


type Msg = Increment | Decrement

update : Msg -> Model -> Model
update msg model =
  case msg of
    Increment ->
      model + 1

    Decrement ->
      model - 1


view : Model -> Html Msg
view model =
  div []
    [ button [ onClick Decrement ] [ text "-" ]
    , div [] [ text (toString model) ]
    , button [ onClick Increment ] [ text "+" ]

Don’t worry about the meaning of that code just yet; we will use it just to test our setup.

The first thing we should check is that we are getting the syntax highlighting in our editor, if you just see plain text, check the corresponding section of this article again.

Next, we are going to test the compilation of Elm code using Reactor. In the terminal, go to the project directory and execute the following command:

elm reactor

A message should appear with something like this:

elm-reactor 0.18.0                                                                                                                                                                   
Listening on http://localhost:8000/  

That means that it is working and we have a web server listening on the port 8000 in localhost. If we open that address in our web browser we should see a file navigator, click in the first-application.elm file and it will build our project. Once it is ready, we will see a page with a little widget with a number and two buttons. That’s the result of our code.

Elm example: Number counter


We have explored some of the advantages of functional programming and the Elm language, and we are now more familiar with the platform that we will use to build our projects.

To get more detailed information about Elm, you can check out the book An Introduction to Elm by Evan Czaplicki.