I have been a professional developer for almost 40 years now and learned more languages than I can enumerate. I prefer programming languages that have a clear idea of what it is. That is one reason why I fell in love with Elm. Loving a programming tool means that you spend a lot of your spare time with it, despite there is no connection to your day time job. Like love, there is no learning without pain, and Elm delivers that in an exquisite way.
Elm is a strongly typed language
To me, that is good news. Strong typing reduces mistakes and enhances tool support. Elm has done this well. The language is designed to allow the compiler to infer types from context without sacrificing security. When an Elm program compiles, it works. If it does not compile, the error messages are in a new class of user friendliness. You have not seen anything remotely like it before.
Let us look at a code example. Suppose you have to handle an incoming message and wish to take action depending on which message you get. We have type “Message” and it can have a fixed set of values, representing the enumerated set of possible messages.
In Elm, this called an Union Type but in my head it is an Enum but slightly better. The best part however comes when you use it in a case statement. Here is a function named “update” that takes two parameters, “msg” and “model”.
update msg model =
case msg of
model + 1
model – 1
If you forget a branch in the case, it is a compiler error! Without the need of any tool support, we have a safe refactoring since changing the type will immediately force the change of the case statement.
HTML is a library
The strong typing is not sacrificed when describing the HTML view. Instead of having a separate HTML-file with placeholders, like you do with template solutions or mixing HTML with the language, like React, Elm has incorporated it in a type safe way using a library.
Here is a function that returns HTML:
view model =
[ button [ onClick Decrease ] [ text “-” ]
, div  [ text (toString model) ]
, button [ onClick Increase ] [ text “+” ]
, div  [button [ onClick Reset ] [ text “Reset” ]]
Every tag follows the pattern of tag-name and two lists, the first is the attributes and the other the contents. Here we have a div tag with no attributes and contents consisting of four other elements. The first is a button which has an attribute that describes what happens when we click on it. We send the decrease message.
We also see that the second elements converts the model to a string and displays it. When the model changes, the result of the view function is changed and the Elm runtime will update the DOM. And it is fast, beating the leader React in many benchmarks.
There are CSS and SVG libraries as well, which works at the same principles as the HTML library.
Now you learned quite a few things, you know how lists and strings look like. You know that Elm gives you type-safe HTML, CSS and SVG. You know the first, but not all, about Union types. The case statement hinted about how the update works but foremost you got a first feel for how strict the language is, you can not forget a case branch.
There are no runtime exceptions in Elm
In Elm there is neither null or exceptions. In lieu with what I described above on case statements, Elm ensures that you always handle all cases. Instead of null there is Maybe which is pretty much the same as an Optional in Java, but with the compiler enforcing you to handle both cases.
case model.drag of
Just drag ->
— Do something
— Handle nothing
So every time it is called upon to handle the possibility of maybe no data, you use the Maybe type which is either Nothing or Just value.
Note that Maybe is a Union type and you just learned a new thing about them, the values does not necessarily look similar to each other. Here is the Maybe type:
type Maybe value
= Just value
Did you see the type parameter, “value”, similar to generics in Java? You learned another thing about types in Elm.
Instead of exceptions we have the type Result. This gives you the compile time check for handling error cases.
type Result error value
= Ok value
| Err error
You may think, oops, that is like checked exceptions and we know how hard that can be. Think again, where are exceptions used? Sometimes really weird, like when a query does not return the expected number of items. Sometimes when you enter an illegal state or get malformed input. Sometimes in communication across the network. It is in the latter case, at the borders of your application, where you need to handle errors. In the former cases, it is just bad design. You should not handle a search query with exceptions and you should never end up in an illegal state. Yes, I believe some of the official Java APIs have a bad design. E.g. getSingleResult in JDBC.
Elm is a pure functional language
To a Java programmer, this is a different paradigm than the object oriented, stateful way of programming. Personally, it was not a problem since I programmed in Lisp 30 years ago, the last time functional programming was popular. You probably have no problem accepting that data is immutable. It will a bit harder to understand how to use the Module concept to accomplish encapsulation. But that you can learn later, when your application grows. You do need to forget about inheritance and mutable state.
So far, functions has had their parameter types inferred but you can annotate them with types. The benefits are better documentation and better error messages from the compiler. Type annotations are written before the function as in this example:
isNegative: Int -> Bool
isNegative num =
num < 0
Functions are of course first class citizens and can be passed as parameters. A filter, for instance, will be familiar to the modern Java programmer.
List.filter isNegative [0, 3, -5, 8]
There is a lambda notation that could replace the isNegative function above, but the readability would suffer.
The Elm architecture
Now I hope you learned enough of Elm to become curious about the language and start with a proper tutorial. One of the first thing you will learn is what is called “Elm architecture”. It is a big word for what is a message, view and model design.
An Elm program, shortly put, is a set of three functions,
- The update function that takes the current model and a message and returns a new model.
- The view function which takes the model and returns HTML markup and events.
- The initialise function which initialise the model.
The runtime gives you an efficient rendering of your web application and you can really focus on the logic of your program. To quote the official guide “So the Elm Runtime is in charge of doing stuff. We just transform data.”
Tools and libraries
The eco system is of course something important. I keep using IntelliJ for programming Elm, it is a familiar tool and it works well. You do not get the same set of refactoring tools as when you program in Java, but that is expected. Do not miss elm-format so your code is always formatted according to standard. Install it as a file watcher.
There is a package system that you may recognise if ever typed “npm” at the command line, but this one is a bit better. There is meaning to the version numbers, so called semantic versioning which means that a major version is required to make breaking changes.
A twist to debugging is the time-travel debugger which logs all state changes and lets you jump back in time and look at things.
Testing is done with elm-test but you may find that the need for tests becomes less. Speaking of tests, if you like katas, there is exercism.io which give you test cases for a problem and your task is to get them all green.
There is a library elm-native for writing mobile apps but it is not mature enough for production. Personally, I have hope for browsers being enough for most apps some time in the near future.
If you like your file structure being set up for you, there is an elm-create-app which was inspired by create-react-app.
The first question is usually “can it be used in production”. Apparently so, as it is already. But that is a really low bar most things passes. With the right backing, you could get lots of stuff out in production, despite being awful in so many ways.
With that said, it feels like early days still. We have not seen version 1.0 yet. The flip side of that observation is that Elm is developing in its own pace, with each step taken carefully.
What I have found hard is the encoding and decoding of JSON. It is by its design, on purpose, a bit loose, if you see what I mean. When it comes to Java, we can use reflection and object mappers to transform JSON to Java objects. In Elm, it is more of a handmade thing. Of course it pays off to exercise your brain around how to match some JSON with Elm records, but it is not usually a core thing of my application. I just want to get the result of a REST call into something I can work with. Do not miss the decoding pipeline when you come to this.
Thanks for reading this far. Your next stop could be the official guide: https://guide.elm-lang.org/