Glu Blog

Getting started with Elm

So what is Elm? It's a functional programming language for creating browser-based interfaces. It generates HTML, CSS, and Javascript so that the code you write is usable across browsers without any special plugins.

Why not just write HTML, CSS, and Javascript like you always have? The primary appeal for me is one about maintainability and stability. Elm has static types, immutable data structures, and supports reactive functional programming. The largest Javascript projects I've worked on seem to eventually converge on some approximation of what Elm calls "signals".

Installing Elm

There's an NPM package to install what's required if you have npm on your system (if not there's also OS-specific binaries):

1
$ npm install -g elm

That installs the elm command, which has a series of sub-commands that will do all of the various things you'll need. If you're starting a new project you can run elm-package to setup the current directory with all of the base packages you need to get started, e.g.:

1
2
3
4
5
6
7
8
9
$ elm package install
Some new packages are needed. Here is the upgrade plan.

  Install:
    elm-lang/core 3.0.0

Do you approve of this plan? (y/n) y
Downloading elm-lang/core
Packages configured successfully!

This will install the elm-core package and will create a project file (elm-package.json). In the project file you define the project name, author, license, dependencies, etc.

There's also a handy REPL for playing locally with syntax to see what happens:

1
2
3
4
5
$ elm repl
---- elm repl 0.16.0 -----------------------------------------------------------
 :help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
>

The last of the tools we'll cover for now is the elm-reactor, which starts a webserver in the current directory and allows you to browse the available files to open an .elm file and evaluate it. Just run:

1
2
3
$ elm-reactor
elm reactor 0.16.0
Listening on http://0.0.0.0:8000/

And visit http://0.0.0.0:8000.

Setting up vim

I use vim as my editor. If you don't, skip this section :).

Install the elm.vim package by (assuming you're using Pathogen) adding the following to your .vimrc:

1
Plugin 'lambdatoast/elm.git'

Save and reload your .vimrc, and run :PluginInstall

Understanding the examples

There are some useful Elm examples to get you started. Or if you don't want to clone the git repo you can play with them in the Elm playground.

I'm going to skip right on over "Hello World!" and "Mouse" and get straight into breaking down how Clock.elm works line by line.

Elm imports

1
2
3
4
import Color exposing (..)
import Graphics.Collage exposing (..)
import Graphics.Element exposing (..)
import Time exposing (second)

We see three different approaches to importing existing modules. Lets break those approaches down:

1
import Color exposing (..)

This is what Elm calls an "unqualified dependency". That is the (..) will import the entire module. It's not a great approach if it can be at all avoided, because it's going to blow out the size of your dependency graph with a bunch of code you'll almost certainly never use.

The next two lines take a slightly more explicit approach:

1
2
import Graphics.Collage exposing (..)
import Graphics.Element exposing (..)

It's importing the Collage and Element modules, from within the Graphics namespace. But it's still importing the entire module definitions from Collage and Element. Are you really going to use all of the functions?

Finally we have:

1
import Time exposing (second)

Which imports just the function named second from the Time module. If you need more (e.g., hour) you can provide a comma-separated list.

A Signal is a value that changes over time. It's a bit like a variable, but when it changes it also initiates some action to occur. All that's important right now is the Signal.map function which applies a filter to the new value.

1
2
main =
  Signal.map clock (Time.every second)

Here we have a few things happening. For a start we've defined the entry point of our application (main =). Next we've defined a signal that applies a filter, the filter being the clock function, whenever that signal changes. And finally we've set the value of that signal to be the current time and told it to change every second.

So reconstructing that in the logical order now that we understand the constituent parts:

Now on to that clock function:

1
2
3
4
5
6
7
8
clock t =
  collage 400 400
    [ filled    lightGrey   (ngon 12 110)
    , outlined (solid grey) (ngon 12 110)
    , hand orange   100  t
    , hand charcoal 100 (t/60)
    , hand charcoal 60  (t/720)
    ]

It accepts a single argument t, and then calls Graphics.Collage.collage.

The collage API is for freeform graphics. You can move, rotate, scale, etc. all sorts of forms including lines, shapes, images, and elements. Collages use the same coordinate system you might see in an algebra or physics problem. The origin (0,0) is at the center of the collage, not the top left corner as in some other graphics libraries. Furthermore, the y-axis points up, so moving a form 10 units in the y-axis will move it up on screen – from Graphics.Collage

That function accepts 3 arguments: a width, a height, and a list of 2D forms that describe the content. The output is then an element to render. The width and height are those two 400 values on the 2nd line, so I'll dive straight into the list of forms which is more interesting and constitutes the rest of the snippet.

Depending on your programming language background if you're not familiar with the term "list', it's just an array. As so what we've passed in is an array with 5 values, the first:

1
filled    lightGrey   (ngon 12 110)

Is the result of a call to Graphics.Collage.filled that draws a lightgrey polygon with a total of 12 sides and a radius of 110.

1
outlined (solid grey) (ngon 12 110)

Followed by the result of a call to [Graphics.Collage.outlined] that draws a darker grey line around that polygon.

And now for the time-keeping magic:

1
2
3
    , hand orange   100  t
    , hand charcoal 100 (t/60)
    , hand charcoal 60  (t/720)

We make three calls for a function named hand (which we define later) passing in a color, a length, and the current timestamp (which a calculation applied on the latter two).

Lets cheat a little by taking a look at a running example of this app to see what the result actually looks like. It's pretty obvious the orange hand is the seconds hand on the clock.

The 3rd argument value is actually the at what resolution we adjust hand in question. For the seconds hand we want to adjust it every second so we pass in the current timestamp unadjusted.

The charcoal hand that is as long as the the seconds had must be for minutes. We want to do a full rotation of the clock face once per hour, and so it moves at 2/60th the rate of the seconds hand. So the 4rd argument here is t/60.

Which leaves the hours hand, slightly shorter at 60. I'll have to admit I got hung up on why the value is t/720 for a while, before remembering analog clocks tell the time in the 12-hour format. So the hour hand does a full revolution every 12 hours, or every 720 minutes.

I also found a handy wikipedia article on the Clock angle problem which goes through the math involved in calculating the angles.

All that's left is to dig into the hand function itself:

1
2
3
4
5
6
hand clr len time =
  let angle = degrees (90 - 6 * Time.inSeconds time)
  in
      segment (0,0) (fromPolar (len,angle))
        |> traced (solid clr)

It expects three argument: clr (color), len length, and time.

It passes the time into Time.inSeconds to return a float that represents the number of seconds in the current timestamp.

We then then do some math (subtracting from 90 because that equation assumes a hand position starting from 12 o'clock moving clockwise, whereas the origination point for the Elm libraries is 3 o'clock), pass it into Basics.degrees and then use a let expression to assign the result to a variable named angle.

Basics is a module of useful functions that get imported by default. Think of it like the Elm standard library.

The in command that follows defines a block of code where the angle variable we just assigned can be used. It's a way to constrain the scope of where our variable is usable and stops things from leaking.

We call Graphics.Collage.segment, passing in two arguments: a pair of numbers to define the origin point of our line and another pair to define where it ends. The first pair is always 0,0 which is the center point of our element. The second set we calculate using Basics.fromPolar to turn our polar coordinates (a radius length and an angle) into cartesian coordinates (an x and y value).

The |> here is the forward function applicator. It's a stylistic convenience method that helps make the kind of code that produces nested callback hell in Javascript more readable. I'll come back to how it's applied here once we've teased out the remaining nested function calls.

We call Graphics.Collage.traced which expects a Linestyle, which we get from passing our preferred color (clr) into Graphics.Collage.solid.

So much nesting of functions! Lets work backwards from the order they're evaluated. You'll have to just trust me a little here as the forward function applicator masks things a little.

Here's what that would look like without the forward function application:

1
traced (solid clr) (segment (0,0) (fromPolar (len,angle)))

I'll break that out with some unnecessary variable assignment and pseudo-Elm code just to remove the parentheses and make it clearer:

1
2
3
4
5
let
  lineStyle = solid clr
  path = segment (0,0) (fromPolar (len, angle))
in
  traced lineStyle path

So the forward function applicator is saying take the result of

1
segment (0,0) (fromPolar (len,angle))

and pass it in as the next argument in a call to:

1
|> traced (solid clr)

If I wanted to refactor the example to apply |> to all of the things I could end up with:

1
2
3
fromPolar (len,angle)
  |> segment (0,0)
    |> traced (solid clr)

And that's it, we've covered every single line in the clock example. And on the back of this code Elm generates for us the HTML, CSS, and Javascript required to display an animated analog clock. Just take a moment to think about how you'd do the equivalent by hand with these tools. The bootstrapping of the base HTML file (which you'd probably just blindly copy & paste from a previous project and then delete all the redundant stuff). Then maybe a basic CSS reset framework to clean things up. And then probably a Javascript library or two to help draw SVGs or something else. I know it'd take me a lot longer to implement than this Elm equivalent.

The thing that I find most exciting here is that my Javascript equivalent would almost certainly have been based around some setTimeout recursive loop that called itself every second. Which is what's happening here given our use of Time.every second. But that could be replaced with anything that changes a value. Maybe it's a server/websocket pushing a change in state? And any time that Signal is updated, Elm takes care of reacting to it and updating the interface. Joy!

Further reading & useful links

The documentation for Elm is quite fantastic. As with most new languages I've learned there's sometimes a learning curve associated with just knowing where to look and the right Google-fu. Hopefully the following save you some of that initial fumbling:

Glenn Gillen

I'm the founder of Glu. I'm also an investor and advisor to early-stage tech startups. Previously worked at Heroku on the Add-ons Marketplace.

We're currently accepting a limited number of new users for our first product, ContentFocus.