Tags

JavaScript

“*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:

— 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 it 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 base 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 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 that 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’ve yet to see anyone writing Elm and not using them personally, for good reason), the compiler will tell you what your annotation should be, so you can literally 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.

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 you as you’re taking your first steps, the compiler will be actively trying to help you sort your code out. 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. 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-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 that 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 here: https://www.youtube.com/watch?v=zBHB9i8e3Kc2

By David Nimmo (@k_fistman)

 

 

Back to Blog

</Follow Us>