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:
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.
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.
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
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.
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.
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. :)