Elm - A Front End Language with Style

The Weekly Iteration

This video is only a short sample, but you can access the full version and all our other great content by subscribing.

Video

Notes

What is Elm?

Elm is a programming language targeted at the front end (runs in a browser, not on Node) that offers a different take on building dynamic web applications. It was designed by Evan Czaplicki to provide "the best of functional programming in your browser".

Some of the more noteworthy features include:

  • Functional (pure) - All inputs and outputs, no state, no side effects, just nice clean functions. For a given input to a function, we will always get the same output, which in theory makes code very easy to reason about.
  • Reactive - Reactive programming is a paradigm in which your UI is defined by a function, and that function is fed a stream of updates from the user actions, creating the reactive cycle.
  • Typed - Elm is a strongly typed language that embraces static typing, similar to Haskell and other ML inspired languages, allowing the compiler to be vastly more useful in helping us create "correct" code.
  • Compiles to JavaScript - Elm code compiles down to JavaScript (which generates HTML and CSS), allowing our code to run in the browser.

Elm Architecture

The Elm Architecture is the recommended structure for building Elm applications. You can see the full source for the sample game in this gist, but to dive into the architecture, it consists of three main parts:

Model

A Model in Elm is a "record", similar to a Ruby hash, which defines the shape of the data our application will operate on.

type alias Model = { x : Float, y : Float, radius: Float }

So here we have our model which will contain the x and y coordinates of our ball, as well the radius.

Update

The update function knows how to respond to any input, or "Action" in Elm speak. Before diving into update itself, we can see how an Elm application defines the possible actions with a "union type" defined for the possible actions. This serves as a great index of the interactive aspects of an application. For instance, for the simple game in which the user can move the ball up, right, or left, we would have an Action type like:

type Action = Left | Up | Right | Noop

From there, we can view the update function. The actual form in the full program is slightly more advanced, but this is a simplified form that ignores the gravity effect:

update : Action -> Model -> Model
update action model =
  case action of
    Noop -> model
    Up -> { model | y <- model.y + 30 }
    Right -> { model | x <- model.x + 5 }
    Left -> { model | x <- model.x - 5 }

The first line of this snippet is the "type annotation" which hints to both any reader (e.g. you 3 weeks from now) and to the compiler what the types of this function are. In this case, we have a function that takes an Action and a Model and returns a Model.

On the next line, we define the actual function, naming the Action argument action, and the Model model (how clever!). We then have a case statement which will select which code branch to apply based on the incoming action. Lastly, for each action, we need to build a new version of the state that has been changed by the action. Note, since Elm is purely function, we can't mutate the state, but instead we need to produce a new state instance with the updated value and return that. Luckily, Elm has a pleasant and concise syntax for producing a new state based on updating a nested value.

View

Since we've collected all of our logic pertaining to how the app changes over time in our Action type and update function, our view function is simply a declaration of how to render the current state of our application. For those familiar with React, this is the same approach, but taken to the extreme.

view : Model -> Element
view model =
  let
    ball =
      circle model.radius
        |> filled blue
        |> move (model.x, model.y)
    background =
      rect (toFloat width) (toFloat height)
        |> filled green
  in
    collage width height [ ball ]

Here again we start with a type annotation; the view is a function taking a Model as the sole input, and returning an Element. In this case Element is a canvas element, but our view could just as easily return HTML elements.

From there, we define the body of the function, building a ball and background canvas elements, and returning a combined "collage" (Elm canvas wrapper) for rendering.

Elm Reactor

With our program defined, now we can try it out. Elm has a utility called the Reactor which can live compile and run our application in the browser. We just run elm reactor in the folder that contains our elm file and open the browser to http://localhost:8000. The Reactor will present a list of all the files. We can run an Elm file by clicking on it, and the application will now take over the screen and run.

Alternately, we can click on the wrench icon next to the file to open our application in the Elm debugger. For this particular script we can see that the model is "watched" in the debugger sidebar allowing us to see the values at any given point in time.

In addition to watching a specific value, the Elm debugger also makes it possible to pause and even rewind the state of our app. Since our Model is immutable, Elm simply holds on to each copy over time and we can rewind, replay, and generally inspect the state of our app at any point.

There are even some fancier features like hot swapping and tracing. Hot swapping allows us to alter our code and see the new versions of our functions live applied to the stream of user inputs. Tracing makes it possible to see a graphical representation of the history of a given value. Unfortunately there are some bugs with this functionality in the current version of Elm, but you can check it out live in the Mario example on the elm-lang website. This functionality was heavily inspired by Bret Victor's talk - Inventing on Principle, which is spectacular.

Elm Developer Experience

One of the stand out aspects of Elm is the consistent focus on providing a pleasant and helpful feedback via the compiler and overall developer experience. Rather than feeling like you're fighting the compiler, Elm is designed to make the compiler seem like a friend providing helpful hints to guide you in the right direction.

Recently there was a big push to further refine the type related error message to make them more friendly and useful. This update is summed up well in the blog post Compiler Warnings.

This works well for both simple typos, for which Elm can even offer alternatives "Did you Debug.watch instead of Debug.vatch", as well as type mismatches.

Remaining Approachable

Elm takes inspiration from a number of more academic languages like Haskell, but simultaneously makes sure to remain approachable and pragmatic. The author, Evan Czaplicki, is very clear about his desire to make something that is easy to get started with, and then eventually opens up to expose the more complex functional bits inside. In his talk, Let's Be Mainstream, he does a great job of summarizing why he thinks functional programming has traditionally done a poor job of selling itself and making it easy to get started, and the approach he is taking with Elm to hopefully avoid these same pitfalls.

One great example is the start-app package which makes it very easy to build an application using the Elm Architecture approach of Model, View, Update, and then letting start-app take care of wiring those functions up to the real world with all its messy side effects and mutability.

Additionally, because of the focus of the front end and targeting the browser as the primary run time, we're in a position to make "real apps" that do "real things", rather than having to play around with simple toy command line apps.

Package manager

Elm comes with a standard library, but it is reasonably conservative in what it includes. Instead, things as core as the HTML library are provided via packages. Even the standard library, elm-core, is distributed as a package. The package manager is a first-class par of the Elm ecosystem.

One great feature of the package system is that it is able to enforce semver to some extent. It does this by inspecting and comparing the type signatures of functions between versions and ensuring the larger changes are sufficiently flagged via the version bump. Prettttttyyy cooool.

Similar Ideas

While Elm is still relatively young relative other programming languages, there is an interesting convergence happening with a number of the ideas Elm is built on. React embraces the idea of your UI being a function on your application state, Redux is a state management library for React that is very similar to the Elm Architecture, Om is a ClojurScript interface to React, and Immutable JS is a library from Facebook for bringing immutability to our JS apps.

Getting Started

If you're interested in getting started with Elm, start with the install guide, then check out the Quick Start guides on the Elm Lang docs site. The Elm Architecture guide is particularly interesting, if only to get exposure to that approach to building dynamic UIs.

×

15 Full Courses, 100+ Screencasts & New Content Weekly