The Basics

Syntactically, Elm looks like many functional language (and Python) out there – no curly brackets, whitespace matters, etc. – which is perhaps unsurprising as there’s seems to be a strong Haskell influence here.

Tuples

Tuples are enclosed in parentheses and separated by comma (,) – e.g. ( 4, “3” ) is a tuple of the integer 4 and the string “3”.

You can also use the helper functions (,) (,,) (,,,) … etc. to construct tuples with the required number of items.

Records

At first glance Elm’s record type looks very similar to F#’s record type:

both are lightweight labelled data structure

both support pattern matching

fields are immutable (but F# supports optional mutability)

both support copy-and-update semantics to create a new record based on an existing record

both support polymorphic functions (though in F# it’s more idiomatic to use members instead)

However, upon closer inspection you find some interesting additions with Elm, most notably:

extensibility – besides the copy-and-update semantic, you can also add and remove fields at the same time

records uses structural typing – a function can accept records of any kind so long they have the required fields (e.g. whoAreYou { name } = … takes any record that has a name field)

record type aliases can be composed together

Have a look at my last post to see a more detailed run-down of the similarities and differences between records in F# and Elm.

Algebraic Data Structure

Elm supports algebraic data structures (i.e. F# discriminated unions), there are a number of built-in algebraic types such as the Maybe type. You can also define your own algebraic types using the data keyword.

F# Pipes

It’s also nice to see F#’s pipes make an appearance in Elm and that Elm’s creators are receptive to adopting F#’s functional composition operators << and >> too, if adopted I really think they can make code that composes functions more readable and intuitive.

Pattern Matching

Elm supports the case..of syntax (another example of its Haskell heritage) for pattern matching against variables as you can see from the snippet above.

Elm doesn’t have when guards in its pattern matching syntax yet, but you can work around this by using the multi-way if expressions for now, which actually reads like pattern match clauses rather than the traditional if-elif-elif-else.

Elm also supports pattern matching against tuples, records and lists, etc. in much the same way as you’ve seen in F# or Haskell, for instance, you can pattern match against a record using the { fieldName1, fieldName2, … } syntax:

The standard library also has two helper functions – curry and uncurry – to help you change how arguments are passed to a function, which comes in handy sometimes.

curry : ( ( a, b ) –> c ) –> a –> b –> c

uncurry : ( a –> b –> c ) –> ( a, b ) –> c

let..in syntax

Like many other functional languages (Haskell, F#, etc.), in Elm the last expression of a function provides its return value and whilst F# has moved away from the let..in syntax from its own OCaml roots, Elm still uses the let..in syntax.

You can specify multiple variables within one let clause, e.g.

And you can also define inner functions in the same way too:

JS Interop

Interop with Javascript is provided via ports, which I haven’t played around with much, but you can read all about it here.

The Good stuff

In Elm, much of what you do revolves around Signals (which is Elm’s equivalent of Rx’s observables) and the language (along with its standard libraries) gives you a very different way of thinking about GUI development to the traditional DOM based approaches. If you are familiar with Rx (or RxJs, the Javascript port of the Rx API) or functional reactive programming* in general then you should feel right at home here.

Elm’s standard library comes with a number of built-in signals – mouse positions, clicks, window dimensions, timer events, etc. From these built-in signals you can then compose and map over them, the built-in Signal library provides quite a number of functions to make life easy there.

Lift functions

You can use the lift functions (lift, lift2, lift3, … lift8) to apply a normal function over signals. In Rx you’d do this with Observable.Select, but the way you can combine signals together using the shorthand operators <~ and ~ is really quite elegant.

For instance, you can pass the signals of your mouse position and the coordinate of your clicks into a function like this:

And here is what the code above generates:

Pretty simple, huh?

Merging/combining signals

You can use the merge, merges or combine functions from the Signal standard library to concatenate multiple signals of the same type into one unified signal. This is equivalent to Rx’s Observable.Concat operation.

This comes handy when you have multiple sources of inputs of the same kind, or when you use algebraic data type to help unify input signals of different kinds.

Take the Missile Command game below for instance, the game loop follows the simple pattern of: action –> state transition –> redraw updated state. However, there are three different kinds of input that would trigger state transition:

frames – to draw the game at 50 FRS means every 20ms we need to move the missile along a certain distance along its path as dictated by its speed

player actions – when the player clicks on the screen we should launch a new missile towards the coordinate where the user clicked

enemy missiles – every x seconds we need to launch a random number of enemy missiles

Signals representing each of these input types can then be merged together with the merge function and passed into the main game transition logic.

In addition, the Signal library also provides a set of useful functions for composing signals, including foldp, count, keepIf, dropIf, keepWhen, and sampleOn. Again, all of these can be mapped directly to Rx extension methods.

In fact, all the capabilities of working with signals I have seen in Elm can be found in Rx and by extension, in C#, F#, Java, Javascript and Dart thanks to the widespread reach of the Rx API and the effort by the developer community to adopt it.

However, because Rx exist as a library in those languages, it doesn’t force you to fundamentally change the way you view UI development because you always have a way out whenever things get uncomfortable (which should be often, if you’re challenging yourself to go outside your comfort zone). In Elm, however, there’s no other way, so you have to think ‘reactive’ all the way through. From a practical point of view this might sound restrictive, but there’s really no better way to learn the functional reactive paradigm than to fully immerse and commit yourself to it.

The Debugger

If you have been following Bret Victor’s work in recent years (which amongst others have inspired projects such as LightTable, Khan academy’s UI design as well as Elm’s online debugger), you might have had to repeatedly scrape pieces of your brain from the walls thanks to Bret’s superawesomedemos.

I recommend giving this page a good read to see how it works and how Elm makes it possible.

Here’s a short demo video to show its time-travelling capabilities in action.

The Not So Good stuff

The language is still very young so expect lots of breaking changes and broken examples from around the internet as it evolves. Personally, I think this is a good thing, it’s better for the language to learn and evolve away from its early mistakes than to forever live with the sins of its youth.

IDE tooling is lacking. Whilst the online editor and debugger can provide hints (i.e. function signatures and a link to the online doc for the function, see below) for functions and types from the standard libraries, they don’t work on user-defined functions and types. You can also just use any text editor such as Notepad++ or Sublime Text that gives you syntax highlighting (Haskell highlighting usually works pretty well) but I usually look for much more support from my development environment and seeing Bret Victor’s work only raises my expectations and Elm’s online debugger is a decent first step.

If you’re developing with the online debugger then be sure to save your changes regularly, to a source controlled file preferably. As the debugger executes your code it collects events along the way, which is how it enables this awesome time-travelling capability to go back in time to see the effect of your changes as you make them. With the Auto-Update option turned on you get to see your changes reflected right away which is really nice, the downside being that if it has collected a fair number of events it can hang/crash the browser and you lose your unsaved changes along the way. Hopefully that’s a painful lesson you won’t have to learn the hard way.

There are a fair amount of guided tutorials for the basic things, but once you start to get into the intermediate/advanced topics you’re mostly left to work out how to do things based on available examples that are barely commented and those auto-generated docs for libraries really don’t do the readers justice. This is common for new languages, I have had the same experience with Dart too despite Dart being much more mature and have a bigger community around it (and not to mention having a company like Google behind it).

Missile Command

Finally, to aid my learning process, I put together a simple implementation of “Missile Command” which was fun to do and helped give me a sense of direction when trying to figure out what I can and cannot do in Elm.

I have written this in a way that’s perhaps more verbose than the official examples, with lots of type annotations to make up for the lack of support from the online editor/debugger. Even so, the source code came in at less than 250 lines, which is really encouraging, imagine the level of productivity a more competent Elm developer is able to achieve!

* on the topic of reactive programming, I came across this talk by Erik Meijer, the father of Rx, titled Duality and the end of Reactive and it’s well worth a watch. Without going into too much detail and robbing you of the joy of making your own discoveries and conclusions from it, my key takeaway from the talk came from Erik’s closing remark:

Reactive is Dead, long live composing side effects.

Now, I don’t think Erik is telling us to stop using Rx or the reactive paradigm that he has done so much to popularize, but to shift our focus from the act of doing them (composing signals/observables and then writing code that reacts to them) to the why – which is to help us rationalize side effects and compose them, and making the implicit relationships between side effects explicit.

Hello there, Yan. I’m researching Elm right now while we’re reading the Elm chapter in “Seven More Languages in Seven Weeks” in a developer book club at work. This blog post is a treasure trove of helpful information! Thank you for taking the time!

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.