Getting started with 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):
$ 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.:
This will install the
elm-core package and will create a project file (
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:
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:
And visit http://0.0.0.0:8000.
Setting up vim
I use vim as my editor. If you don't, skip this section :).
Save and reload your
.vimrc, and run
Understanding the examples
I'm going to skip right on over "Hello World!" and "Mouse" and get straight into breaking
Clock.elm works line by line.
We see three different approaches to importing existing modules. Lets break those approaches down:
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:
It's importing the
Element modules, from within the
But it's still importing the entire module definitions from
Are you really going to use all of the functions?
Finally we have:
import Time exposing (second)
Which imports just the function named
second from the
Time module. If you need
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.mapfunction which applies a filter to the new value.
Here we have a few things happening. For a start we've defined the entry point of our
main =). Next we've defined a signal that applies a filter, the filter
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:
- Set a value to the current time…
- change it every second…
- and each time you change it run the
Now on to that
It accepts a single argument
t, and then calls
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:
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
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:
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.
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
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:
It expects three argument:
len length, and
It passes the
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
Basicsis a module of useful functions that get imported by default. Think of it like the Elm standard library.
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.
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).
|> here is the forward function applicator.
I'll come back to how it's applied here once we've teased out the remaining nested function calls.
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.
solid clrto get a
- which is passed into
(0,0)to get a
Pathare then passed into
tracedto return a
Here's what that would look like without the forward function application:
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:
So the forward function applicator is saying take the result of
segment (0,0) (fromPolar (len,angle))
and pass it in as the next argument in a call to:
|> traced (solid clr)
If I wanted to refactor the example to apply
|> to all of the things I could end up with:
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:
- Elm Core Library docs: make liberal use of the search box on the right to find where various functions are defined.
- Elm Syntax: it's only about a 10min read. Do it from end-to-end so you at least know the terminology.
- Elm Architecture Tutorial: now that you've got your head around the expanded hello world example this is going to make much more sense.