The road to Elm

I watched a presentation on Elm some time ago, and thought: “This is pretty neat, but ‘signals’, ‘lifting’, ‘FRP’ … There’s just too much new stuff, and I don’t even know Lisp.”. I didn’t really feel intimidated, but I wanted to learn things “in order”, and I perceived Elm as something best consumed later, after a steady diet of mainstream functional languages. In hindsight, I was foolish, but that was the mindset I carried, and I continued on to dabble with Common Lisp, Scheme, Clojure, and Haskell, before ultimately deciding to revisit Elm.

The first three languages were a shock to the system, not because the fundamental concepts were difficult to understand, but because the code was so incredibly difficult to read. Initially, I reasoned that I’m simply not accustomed to code of such high density, and that this would become a lesser problem as time passed. And sure enough, over time, the difficulty decreased, but it was still far from easy. “What was my difficulty?”, I wondered, additionally asking on various lists, forums, and other channels. The responses were mixed, but generally, many felt that I simply need to spend more time developing “paren eyes”, and the ability to read “inside out”.

It was at this point that I started playing with Haskell, and it was at this time that I finally realized the source of my strain. It had nothing to do with “all the parens, all over the place”, as those were merely a symptom of the core problem, which was excessively deep nesting. This was not a language issue, as you could write crazy nested expressions in Haskell, just as in those other Lisp derivatives, but Haskell programmers refrained from doing so (for the most part, based on the code I sampled), and this, I felt, was the fundamental difference; In deciding not to abuse lambdas, one can reap the expressive benefits of a functional language, without having to develop “special eyes”.

That said, being a “pure” functional language, Haskell delivers a new set of issues to ponder, like Monads, and the incredible confusion that has been riled around them. There are also very few interesting examples to learn from, and many tutorials focus too much on the language (especially the pure aspects), and far too little on actually writing software in Haskell (which tends to include non-trivial state, which is managed via Monads, which are poorly explained).

Still, I continued playing with Haskell, and I eventually wanted to build a more complex program, with a graphical interface. I researched how one would do something along those lines in Haskell, and I found several articles that extolled the virtues of Functional Reactive Programming (in this context). There were many libraries that offered FRP system features, but having to download them, and then wrapping my head around the basic principles they enshrine … “Hey, what about Elm?”, I thought, as I remembered this young language, built with FRP principles at the core. I figured that the learning curve would be about the same, and that I was therefore ready to jump in.

The Elm web site was inviting, and as one would expect with a language that compiles to JavaScript, there were various graphical demos to play with, directly in the browser, via a simple editor. You could really take the language “out for a spin”, without having to download anything, and this was great, but there seemed to be no language tutorial. The syntax was a Haskell derivative, but not to the point where Haskell tutorials could be used in place.

My giddiness was quickly replaced by disappointment, and I was about to abandon Elm, but then I actually opened the mouse position demo, which was just a small program that rendered the current mouse coordinates, tracking updates as they changed. This program was a one-liner (not including the import directive, to which there was no mystery), and so, I thought: “I could probably understand this, even if I don’t understand the language.”.

I started out by lifting asText into various signals. The different values were displayed, and updated with relevant events. I tried lifting asText into more than one signal, and I learned from the resulting error. I reduced larger demos to their smallest working pieces, learning how they actually worked. I understood foldp, and with the ability to preserve state, I created a small game, in just under 100 lines; It was a pleasure to write, but why? Was it the expressive nature, or the overall elegance of the language? What about those moments of enlightenment when a perfect composition becomes clear, and you solve a fairly involved problem with just one perfectly clear line? Yes, but these are only obvious because they were made as such, in an environment that reduces interactive friction to a minimum.

When given a fun playpen, people can’t help but play, because that’s how we’re really wired to learn.

Everything else is basically automatic, so you can make things work even without any other materials. Although, additional material can only help, so I decided to contribute with a series:

Five videos, around 30 minutes each, covering the development of a small avoider/dodge game in Elm. You can read/play the game here: https://github.com/GoranM/bluepill

If you plan on learning Elm, this may help you do so. If you don’t plan on learning Elm, here’s why you should:

Software is already far too complex, and this is not going to change in the near future. Typical object oriented languages don’t provide strong mechanisms to properly manage state. If everything can potentially change, everywhere, at any time, it’s really difficult to be sure of anything, so one must build systems to enforce various guarantees, and this leads to more code, more involved modules, and more complexity. Elm, like Haskell, places significant restraints on the programmer, but in doing so it avoids uncertainty: State can only change in the signal stream - everything else is just pure code that operates on received values, and evaluates to some other value. Managing state is (in large part) managing complexity, and with Elm, you can do it better.

The expressive nature of functional languages is equally important: The game I created is fairly trivial, but try writing this in any other language, and you will need additional libraries, chunks of initialization code, and who knows what else. In Elm, it’s just around 100 lines, with nothing lost on clarity. By the time someone actually figures out all the boilerplate code required to make a window show up in some other language, the Elm version is complete, and that’s real leverage -> You write less code, to do more, and do it faster, with fewer bugs.

The type system: Very strong in Elm, as in Haskell. If you can get it to compile, it’s probably fine.

The web: It’s everywhere, and Elm lives in it, so that should answer the “portability” question.

Not to say that it’s perfect: Elm is very young, so there will be bugs, and there is still plenty of work to do, but it has tremendous potential, and a very reasoned (not to mention capable) community.

So, my advice in short: Learn Elm now, and thank me later.