“Sigh…another language that compiles to JavaScript. Pass”

That's how quickly I initially dismissed Elm when I first heard about it a couple of years ago, mostly because I was mentally likening it to Coffeescript, a language which I don't mind saying I wasn't a big fan of. And I imagine I'm not the only person to make that mistake, so I figured I'd write a quick post about why I've ended up going all-in with Elm, and why I think the language deserves your time.

So here are the highlights:

Note: all comparisons are to JavaScript, seeing as Elm is a front-end tool, and JS is the biggest game in that particular town

Everything is immutable

Whilst functional programming is not a new thing by any means, it has been getting a lot of new fans in recent years, thanks to its lending itself to more concise, more inherently reusable methods. One of the main tenets of FP is that state is very carefully managed, and whilst this is possible to achieve in JavaScript, it requires discipline and buy-in from every developer that touches the codebase, since JavaScript will let you do anything you want.

Elm makes this a lot easier, because it just doesn't give you the tools to mutate anything outside of the function that you're working in, and wherever a constant needs to be updated inside a function, a new constant is created, rather than overwriting the old one. This ensures that functions will always have the same outputs when given the same inputs (another important principle of FP), and it also makes your code inherently testable. You don't have to worry about accidentally braiding your code together, because you just don't really have that ability.

Every function has a return value

In JavaScript, you can ensure all of your functions have a return value yourself, or you can choose not to, and give yourself the ability to call those functions from anywhere you like. This sounds great doesn't it? Flexibility is awesome. Until you read your code back after a weekend, and you come across a function being called in the middle of another function, and you have to track it down to figure out what's happening, and inside that function there's another function being called, and you have to go and read that one too. Or you find that you've written a function that has a return value but also causes some other side effect inside it, and now you have to follow two trails just to remind yourself what you were thinking on Friday afternoon.Â

Elm doesn't give you the option to get yourself into this mess. Every function in Elm acts as an expression (which, as mentioned earlier, can't control anything outside of itself), and returns the value of that expression (consider the difference between having return statements inside an if block vs just actually returning the if block in JS). The effect this has on the readability of your code is fantasticâ€Š–â€Šyou know that no matter what, you already have a basic idea of how functions need to be used.

Every function is auto-curried

In JavaScript, you can partially apply a function by using a closure to return a function call with x number of parameters already applied. It's trivial to accomplish, but without understanding closures it isn't easily readable, and can be confusing for less experienced developers when they see this in your code.

In Elm, every function will do this for you automatically. If you call a function without all of its required arguments, you'll be returned a function which will take the remaining arguments. So if you have a function called multiply which takes x and y as parameters, and returns x * y , making a double function is as simple as double = multiply 2 . That's it. And then you can call double 10 and it will give you 20 . Nothing else required. So it's double = x => multiply(2, x) (JS) vs double = multiply 2 (Elm). For my money, the Elm code is a lot cleaner and simpler to understand.

Types, and type annotations

Forrest Gump's most memorable quote was “Life is like a JavaScript application: you never know what you're gonna get”. Well I'm pretty sure it was about JS anyway; it's been a while since I've seen that film. But the point still stands; when you're looking at a JS function that you haven't written (or even one you have written), you don't necessarily know what you're going to see there. It may or may not be using JSDoc to document its input and output types, and if it is, it may or may not be up to date, and there's no guarantee that everything else will be documented even if the function you're currently looking at is. Maybe you'll find that TypeScript is being used on this project, and again the type definitions may or may not be up to date (seeing as TypeScript will still compile for you even if the types you put in aren't what you said they were going to be). Or maybe the development team is really into coercion and intentionally doesn't care about types wherever they don't strictly have to.

Elm is strongly-typed, and has fantastic support for type annotations. Annotations appear above functions as easily readable statements; for example, for the multiply function mentioned earlier, the type annotation would be multiplyÂ : Int -> Int -> Int . If that looks weird, remember what I said about auto-currying. You can read that annotation as “takes two integers and returns an integer” (since the last value will always be the return value), but what it's really saying is “takes an integer and returns a function that takes an integer and returns an integer”; hence the same symbol being used for both parameters and the return value.

Annotations are evaluated by the compiler, so if you write a function and get its type annotation wrong, you will know about it when you try to compile your code, by way of a very easy to understand compilation error. And if you don't write an annotation for your function (remember I said they were optionalâ€Š–â€Šalthough I personally have yet to see anyone writing Elm and not using them, for good reason), the compiler will tell you what your annotation should be, so you can just copy and paste it if you're unsure.

You can also create your own types by way of type aliases, so your annotations can be even more readable if you create type aliases for things that are relevant to your application. For example, you might be writing an e-commerce application that has a Cart which contains Items , making your codebase even easier to understand to anyone who is unfamiliar with it.

The compiler

I've touched on this already, but Elm's compiler is incredibly useful; to the point where you find yourself with a lot of confidence that you know your application is working as you expected as long as it's compiled. It also makes the language a lot easier to pick up, because as you're taking your first steps, the compiler will be actively trying to help you sort your code out. Elm promises to help you "Generate JavaScript with great performance and no runtime exceptions", and in my experience so far it's living up to that promise nicely.

Closing thoughts

As you can tell, I'm really into Elm, and I think it has a bright future. It's fun (to the point where I frequently find myself finishing bits of functionality before I feel mentally “ready” to be finished!), and refreshing, and it has a strong and supportive community behind it. It's easy to get started with, and you can be confident that your applications are robust and performant thanks to the compiler and the built-in virtual DOM respectively.

And to be honest, it's nice to have a genuinely viable alternative to JS for the front-end. Don't get me wrong, I love JS as well; the tone of this post's comparisons between Elm and JS is only the way it is because, in my opinion, Elm compares so favourably in every instance. That's why I'm not currently spending any time writing anything other than Elm for front-end development anywhere I don't have to.

There is a lot more to love about Elm than I've covered here. If you're interested in finding out more, I'd highly recommend you start with Richard Feldman's excellent "Introduction to Elm" talk, which you can find on Youtube.

I hope to see many more people developing in Elm in the future. I genuinely think your life will be better if you decide to join in. :)