I recently wrote my first application in Elm, which is promoted as a “delightful language for reliable webapps”.
The application is an accessible color palette builder, which builds on the excellent design of the 18F Visual Identity Guide to provide visual designers with real-time feedback on the accessibility of their palettes:
Elm is elegantly simple
What strikes me most about Elm is how easy it is to learn, and how quickly one can start using it to build useful things. This can be done through Elm’s official guide, though I personally used Elm: The Pragmatic Way by The Pragmatic Studio, which I highly recommend.
When I first learned Elm in March of 2016, I actually wouldn’t have been so quick to recommend it: at that time, building anything useful required learning about something called signals, which was a significant conceptual hurdle; but after signals were dropped in Elm 0.17, things got a lot simpler. This illustrates one of my favorite things about the design of Elm: its creator and community are constantly trying to make it easier to learn without losing any of its power.
Even if one doesn’t end up using Elm in their production code, I think Elm might actually make it easier to learn concepts that can still be used in JavaScript. For any JavaScript programmers who might currently be overwhelmed by the sheer cognitive load introduced by React, Redux, TypeScript, and concepts like immutability and functional programming, I highly recommend dabbling in Elm. It’s a lot simpler than the analogous set of JS tooling, and it’s often just as (if not more) powerful. And even if you decide to ultimately go with the JS tooling instead, it should be easier because you’ll already have learned the fundamental concepts in Elm.
All that said, though, writing my first app in Elm wasn’t without its share of difficulties.
Interacting with DOM-based JavaScript
Easily the biggest frustration of using Elm was that, while it does include robust support for interoperating with legacy JavaScript code, its virtual DOM implementation currently has no analogue to React’s component lifecycle. This feature of React has been critical for me when it comes to integrating third-party JS widgets with my code, as it essentially serves as an “escape hatch” from the virtual DOM. The lack of such a feature in Elm made it difficult to integrate jscolor into my app.
A nice standards-driven alternative to component lifecycle methods might have been the use of HTML5 Custom Elements; however, Elm’s virtual DOM has no support for this either–though I suspect it wouldn’t be hard to add, and I’d be interested in contributing support for it, if it’s something the community agrees with.
Debugging
One of the most promoted advantages of Elm is that it actually has no runtime exceptions. That is, unless your code explicitly calls out to JavaScript for some of its functionality, it is impossible for a line of code to cause your program to “crash” and become unusable. This is something that even TypeScript has a hard time guaranteeing (though it does still reduce runtime exceptions significantly).
However, this isn’t to say that one’s code will be free of bugs: accidentally using a <
instead of a >
when comparing two numbers will obviously result in unintentional behavior that no static analyzer could detect. And due to Elm’s nature as a functional programming language, I had some trouble figuring out exactly how to debug my program in the few instances that my program went awry. I had no access to a conventional debugger, and adding logging statements into the midst of my code sometimes felt like an engineering feat by itself, but this could easily be due to my unfamiliarity with debugging functional code. Not being able to write statements takes some getting used to!
Outside of that, though, not having to worry about–and constantly guard against–every line of my code potentially throwing an exception was still an enormous weight off my back. And Elm’s ridiculously friendly error messages made conversing with the compiler delightful.
Some skepticism
While Elm was enjoyable to learn and use, I have to admit that I’m not fully sold on the functional notion of “immutability at any cost”. While it’s a great idea in theory, in practice I’m not certain I’ve run into a lot of situations where mutability was a significant source of bugs–as long as I was disciplined about keeping things immutable to a reasonable degree.
I’m also not sure if the limitations posed by being forced to be immutable at all times outweigh the advantages of, say, a language that encourages immutability but allows mutation if needed. An example of the latter might be the language Rust, where data is immutable by default but can be made mutable through the use of a mut
keyword. Like Elm, that language has a similar focus on rock-solid reliability, but does so without restricting the programmer to a purely functional paradigm.
In any case, I’m looking forward to working more with Elm and seeing the language continue to evolve.