Engage on Facebook Engage on Twitter Engage on LinkedIn Engage on GitHub components notes mobile card heart 2 infinite mirror 2 pricing support

Why Elm?

By Brian Dukes

I posted about our team's decision to start using Elm to develop front-end applications. That post deals with the high level benefits of using the language, but what does it really mean to be a developer working in this language?

Elm is a functional language, which means you'll run into a few concepts that you have to tackle, but Elm as a community is also very intentional about being mainstream-friendly, so if that word functional sounds too fancy or weird or difficult or niche, I'd urge you to give it a try. You will have to change the way you think about some things, but you'll do it with helpful tutorials, documentation, and community.

Elm has a small set of features which you can learn quickly, and because its feature set is constrained, it tends to guide you in the direction of writing clean, well-architected code. The Elm community prefers to have one way to do things, so you're not left guessing.


In my opinion, the big concept that you have to wrap your head around in order to be productive in Elm is immutability. Elm enforces immutability, which means that instead of changing existing values you return new ones. Because nothing ever changes after it's created, you have some wonderful guarantees when reasoning about your program, as well as guarantees that the language can take advantage of to optimize your program in interesting ways. However, if you're used to using loops (with a counter that changes) or poking into shared global state, you'll have to learn new techniques. However, much of time you may find that you're already familiar with these techniques, because many mainstream languages have been borrowing functional concepts over the past few years. For example, you'll replace that loop with something that looks very much like LINQ.

As a quick example, if you want the cost of all green apples, it might look something like this:

 myApples |> List.filter (\apple -> apple.color == Green) |> List.map (\apple -> apple.price) |> List.sum

However, there are places where what you want to do isn't already wrapped up in a function and you'll have to dust off your Comp Sci 101 book and do some recursion.

Type System

Elm is a statically typed language, which is one of its great strengths. This means that when you try to pass a String to a function that expects an Int, it will tell you that you can't do that. However, the compiler is designed to be an assistant, not an adversary, and has best-in-class error messages for when your code is trying to do the wrong thing. It's much better to find that out at compile time than at run time. The common refrain that you hear from Elm developers is that once you satisfy the compiler, your code just works (i.e. if it compiles, the only thing that can go wrong is bad business logic).

Elm has type inference, meaning that you don't have to label your types if you don't want to, Elm will figure out the types your code requires and make sure everything matches (however, you can annotate your functions with types to clarify your intention and constrain the types you allow). This removes a lot of the ceremony that is often the reason developers want to avoid typed languages such as Java and C#. I have yet to run into a scenario in Elm where the compiler wouldn't let me do something with my code that I want to be able to do (which is a common scenario in C#'s type system).

Package Ecosystem

Elm comes with a package manager, so that you can install other Elm libraries easily. The package repository requires adherence to SemVer, meaning that it requires a major version number change when it detects breaking changes in your module's API. This means you can be sure that your code won't break when updating to a new version of a module. Additionally, it requires that published modules start at version 1.0.0 (as opposed to npm, where the vast majority of packages are version 0.x), implying a level of maturity of the code that's been published to the repository.

What About JavaScript?

It's nice that Elm has its own package repository, but it can't compete in all things with npm, which houses 300,000 of packages (though the lack of choice can be considered one of its features). What happens when you have a component written in JavaScript that you still want to use? Elm is designed to work well in diverse environments (i.e. to integrate with your existing JS-based app) while still maintaining its core design principles. It does this by turning JS communication into messages passed into and out of your Elm application. You can call out to JavaScript when you need to, but still maintain confidence in the guarantees that Elm provides to your application.

Why Elm?

Using Elm frees developers from hundreds of tiny worries and questions that they have to address in other languages and environments. Immutability means I never have to question whether it's safe to make a change to a variable or to reuse a value. I can have confidence that what I've written will work regardless of what gets thrown at it. At Engage, we've been embracing immutability in our JavaScript and C# where we can, but the environment that our code runs in means that we still can't ever be confident that what we intend is what happens everywhere. The guarantees of Elm are like a breath of fresh air for developers. Elm is a powerfully freeing tool that we're glad to have in our toolbox and excited to make more use of. We hope you find it useful, too.

Planning a DNN upgrade? Download our guide