My impressions of Elm

If you know me or have been reading this blog for a couple of years then chances are you know I'm a programming language nut. I not only believe that being a polyglot programmer makes you a better programmer overall, but I'm always on the look out for new ideas that may benefit Python in some way. This means that when I get a decent stretch of time I typically try to use it to learn a new programming language (to put this into context, to relax while attending my first OOPSLA conference I learned Icon which where Python got its inspiration for generators).

Since I had to use my vacation or lose it this year, my winter holiday is about two weeks, during which Andrea has to work for part of it. That means I had time to learn a new programming language. I decided to learn Elm as I have a one-page web app I wanted to create which seems perfect for the language's functional, reactive style. So during my vacation I read An Introduction to Elm and coded up my web app.

The good

The first thing I found fascinating about Elm was it has automatically enforced semantic versioning. It's one of those ideas that seems obvious in hindsight. Since Elm is statically typed it can infer when the API of a library changes from a previous version (you can even diff the APIs between versions). That means that from an API perspective you don't have to even think about how to handle a version bump or worry if a library messed up their version number as you can infer programmatically when the API is exactly the same, when something was added to the API, or when some part of the API changed in an incompatible fashion (this obviously doesn't cover semantic changes directly, but if you're asking for a version bump and the API didn't change then there's obviously some bugfix, and if you did add/change something then semantics changes are covered by the version bump anyway). It's rather clever and something other statically typed languages could benefit from also instituting. (I don't see how this could work in Python simply due to the fact that __getattr__() exists and thus there's no good way to define the API programmatically.)

The next interesting bit is the author of Elm has obviously been exposed to Python. In the section introducing the core language there's a discussion of integers and floats and how Elm does it the way Python 3 does it (e.g. 9 / 2 == 4.5).

I also appreciate that the language pushes a reactive programming approach. My first major exposure I had to a view automatically reacting to changes in your model was through Angular2 and a MVVM approach for Which Film. But Elm is able to take all of this even further with its reactive programming architecture by having everything revolve around changes to the model. This allows all asynchronous events to trigger messages and have the model react to those messages. This then cascades to anything that deals with the model after every change to it. And by having Elm itself handle when to fire those messages you end up with a very performant system which can quickly react to changes in the model.

Trying to make the compiler more of an assistant than enforcer helps deal with the functional programming aspect of Elm that's very nice. If you have ever programmed in other functional languages like Haskell or OCaml then you're probably used to some rather cryptic error messages coming out of the compiler. And in the case of functional languages, having cryptic compiler messages is rather bad since these programming languages are often structured such that the compiler can infer and enforce so much and so they also enforce a lot (the amount of inference that's possible is why financial companies sometimes latch on to functional languages as their compilers can make them extremely fast with less effort than in languages where you have to be a bit more explicit). But Elm has put effort into trying to provide more literate error messages to help people solve issues. Elm's approach to compiler error messages is liked so much that it's already inspired Rust to have better compiler error messages. There's also been discussions about how to apply this idea to Python, but as exceptions are used for both error reporting and control flow it's a delicate balance of exception construction cost compared to error message detail.

Doing front-end web development in a functional language is a rather good fit (JavaScript was originally supposed to be "Scheme in the browser"). For instance, Elm only has immutable data with side-effect-free functions (called pure functions in functional language parlance). This allows Elm to have a great debugger where it logs all data transitions and thanks to reactive programming you can simply replay the transition log to repeat exactly what led to a specific situation. Since there's no global state to track that means you don't have to worry about the log missing any details.

Looking at all of these positives, you can tell the language is being designed to try and solve the problem of how to do client-side web programming efficiently and be easy to grasp. The creator of Elm, Evan Czaplicki, gave an interesting talk at a functional programming conference where here clearly points out how gradual learning, communication, usage-driven design, culture, and tooling really drive language adoption. I see some parallels with how Python has done things, and Evan even admits to taking ideas from Python and Ruby to try and grow Elm usage.

The bad

But Elm is obviously not without faults. Probably the biggest one is simply how nascent the language is. For instance, I attended a talk at OSCON 2016 that was supposed to be on signals in Elm, but literally about a week before the conference that paradigm was switched to subscriptions. You can also look at the documentation where there are still some holes, e.g. there's nothing about tasks, and yet you need them for some fundamental things like getting the current time (if you're not waiting on a clock interval).

The ugly (code of mine)

In the past I have learned a ton of languages by reading their documentation and then doing a handful of toy examples to just get a feel for things. The problem with that is while it's enough to get a sense of the flavour of a language and how difficult it is to get started, it doesn't dive into deeper details like how the community is organized. To allow me to dive into a language a bit deeper which allows me to have an opinion without coming off as uninformed, I now solve an actual problem I have in the new language. While this means I learn fewer languages, the ones I do bother learning I have a more well-rounded, informed opinion of.

For Elm I ended up creating a simple, single-page time clock web app that counts down how much time is left in the workday along with accounting for any (un)used lunch time. I find I tend to be more focused on my work when I know how much time I have left in my workday. Andrea also has to record her hours for work and so I figured I could work on a simple web app to assist the both of us. It also fit nicely into Elm's strength of reactive programming as the app basically reacts to the tick of a clock to countdown how much time is left in the workday (or lunch break).

The code for what I created can be found in my time-clock repository on GitHub and the deployed site is at http://time-clock.surge.sh (which I'm still playing with). The key insight for me in this exercise was that beyond messages triggering updates to the model, the model itself communicates with parts of the system when it changes. What this means is that the view produces update messages, which trigger changes to the model. Changes to the model trigger subscriptions, which themselves trigger update messages (commands also produce update messages to the model). So realizing that triggering subscription changes was through changes to the model was key as all other aspects of Elm communicate through update messages.

After that the only other real annoyance was simply the immaturity of the platform due to how young it is. For instance, to set the title of a page you have use the JavaScript interoperability support, otherwise you have to embed Elm in a page instead of letting Elm handle everything because it doesn't expose anything outside of the <body> tag in its view and support for specifying the title hasn't been added to the Html library yet. (In the end I will probably embed the Elm code anyway so I can specify stylesheets for the page.)

Otherwise the experience was rather positive. The compiler messages were very helpful as designed. The syntax was simple and clean. The performance seemed great (albeit my app is simple).

My overall impression

In the end I enjoyed using Elm. It's obvious it has some growing to do (the language is only at version 0.18), but I will be keeping an eye on this language going forward as a competing for my front-end web development attention next to Dart and TypeScript.