Why is Elm such a delightful programming language?

Marcio Frayze - Mar 8 '23 - - Dev Community

A Brazilian Portuguese version is available here.


Elm is a programming language created by Evan Czaplicki and a recurring definition used to describe it is:

"A delightful language for reliable web applications." - Elm website.

I like this description because it shows the philosophy of the language and its community. A formal definition could be something like: Elm is a pure functional programming language with immutable data structure, soundness type system, currying, and blah blah blah. But instead, the author preferred to highlight his real intention: to create a safe language that web developers feel pleasure in using it. The rest is a consequence of that!

But as you may know, there are several other alternatives to JavaScript for webapp development. Most of them (including Elm) are languages that are transpiled into JavaScript. A list of some famous (and not so famous) alternatives include: CoffeScript, TypeScript, Reason, ReScript, PureScript, Blazor, ClojureScript,...

With so many options, what would be Elm's main feature that makes it so delightful and unique?

Elm's main differences

Pure functions

There are several factors that make me like this language so much, but if I had to highlight only one technical feature that makes it so different from other solutions, it would be: Elm is a pure language. This means that all the functions we write in Elm are mandatory pure, i.e. they cannot cause side effects.

This feature forces a different mindset in software development. In a way, it deprives you of a number of possibilities but that, together with an architecture designed for this, manages to make development much more predictable, safe, easy to test and maintain.

When I found out that Elm was a pure language, the first question I asked myself was:

🤔 But how can it be possible to develop a dynamic page without making a REST call? After all, this is a form of side effect!

And yes, that it is possible. Even if all the functions you implement are pure, there is a runtime capable of performing (in a very controlled manner) some types of side effects, including triggering REST services.

This very short and easy to follow guide shows how this is possible. Even if you never develop a real project in Elm, you'll probably find ideas there that you'll be able to reuse in other languages and frameworks. It was only after reading this guide and understanding how Elm's architecture works that I finally understood, for example, how React, Flutter, Redux, MobX and similars work! 😉

But that's not the main feature that makes me like Elm so much.

Friendly error messages

Another exalted feature within the Elm community is it's compiler's ability to generate super-friendly error messages. This is a constant concern and in each new version the messages become clearer. According to this post, the goal is that error messages not only point out the flaws but can also teach the syntax of the language. This helps even more if you are a new to the functional paradigm.

But this is not the main feature that makes me like Elm so much.

Immutability

All data structures are immutable in Elm. Once a value is assigned to a variable, it cannot be changed within the same scope. That is, the following code is invalid in Elm:

x = x + 1
Enter fullscreen mode Exit fullscreen mode

If you enter the Elm REPL (running the elm repl command) and try to run the code above, you receive the following error message:

> x = x + 1
-- CYCLIC DEFINITION ------------------------------------------------------ REPL

The `x` value is defined directly in terms of itself, causing an infinite loop.

2| x = x + 1
   ^
Are you are trying to mutate a variable? Elm does not have mutation, so when I
see x defined in terms of x, I treat it as a recursive definition. Try giving
the new value a new name!

Maybe you DO want a recursive value? To define x we need to know what x is, so
let’s expand it. Wait, but now we need to know what x is, so let’s expand it...
This will keep going infinitely!

Hint: The root problem is often a typo in some variable name, but I recommend
reading <https://elm-lang.org/0.19.1/bad-recursion> for more detailed advice,
especially if you actually do need a recursive value.
Enter fullscreen mode Exit fullscreen mode

The first time I saw this message, I almost fell off my chair. How come I can't increment the value of x??? It was the first language I knew in which this was forbidden! But deep down I always thought that incrementing a variable didn't make any sense. In mathematics, the following system of equations is considered invalid:

x = 1 and x = x + 1

Doesn't it seem obvious that if x has the value 1, it (in this scope) could never be equal to itself plus one? So why was this is so normalized in the programming world?

You may be wondering how you could develop an entire system without being able to change (reassign) variables. And that was one of the reasons I ventured into this language. I had to understand how this would be possible! Over time I have realized how immutability helps me create simpler code and avoids a number of bugs.

But that's still not the main feature that makes me like Elm so much.

No null (or undefined) references

Every JavaScript developer has come across a "undefined is not a function"! As I said before, error messages in Elm are created with great care, so a message as vague and generic as this would never appear in a code made in Elm.

But Elm's compiler goes way beyond that. As I have already described in this article (in pt-BR), the possibility of assigning a null refence to a variable brings a number of problems and became known as "the billion dollar mistake". And Elm took the most direct and sensible attitude to solve this problem: completely eliminated the possibility of creating null or undefined references! Using techniques such as Maybe, the Elm compiler can handle all scenarios where a missing value needs to be represented.

Elm brings other similar concepts as well, but for other purposes, such as the use of Result rather than the infamous idea of exceptions.

But that's still not the main feature that makes me like Elm so much.

No runtime errors (in practice)

Another feature that draws a lot of attention and has a lot of prominence within the Elm community is the absence of errors at runtime.

It is almost impossible to generate unexpected failures while running applications written in Elm. There are reports of systems with more than 600,000 lines of Elm code that do not present unexpected errors (those untreated exceptions) during their execution.

JavaScript programers know that, no matter how hard they try, errors will emerge in unusual situations. In Elm that doesn't happen. If there is a slightly possibility of a problem, the compiler will show where and how it can occur and will force you to deal with all possible situations. It's like programming with someone helping you all the time not to screw up!

I discuss this in more detail in this article.

But that's still not the main feature that makes me like Elm so much.

An amazing package management system

Every JavaScript developer has cursed at NPM at least a few times in their life. Who has never spent hours trying to find an obscure error and then delete the node_modules, download the dependencies again and everything works as if by magic, without ever finding the real source of the problem? At least with me, this has happened numerous times.

And when I have to update JavaScript project dependencies, there's always that butterflies in my stomach! Those strange console error messages and hours trying to find the reason why a function is no longer found. A simple minor update to a library can cause major problems and give me headaches.

Elm's package manager doesn't usually let me down. First, because it forces the use of semantic versioning. For example: if a package maintainer tries to upload a new version with a modified signature of a public function and changes only the last (minor) version number of the package, the upload of the new version will be refused! Thanks to pure functions and an excellent type system, the server is able to catch this and invalidate the submission.

And this also brings us some extra really nice features. It is possible, for example, to compare what has changed from one version of a package to another, using the command elm diff followed by the name and the versions you want to compare:

elm diff Microsoft/elm-json-tree-view 1.0.0 2.0.0
This is a MAJOR change.

---- JsonTree - MAJOR ----

    Changed:
      - parseString : String -> Result String Node
      + parseString : String -> Result Error Node

      - parseValue : Value -> Result String Node
      + parseValue : Value -> Result Error Node
Enter fullscreen mode Exit fullscreen mode

The example above shows that from version 1.0.0 to 2.0.0 of the elm-json-tree-view package, the return type of the parseString function changed. It was Result String Node and became Result Error Node.

I'm not aware of another language that has a package manager that can do this. 😃

Another big advantage over NPM is that all third-party functions imported via Elm packages are also pure. So they are unable to carry out any types of side effects without your consent. This brings a strong guarantee in terms of security. While in NPM, third-party code will be allowed to do whatever it wants, without any kind of control.

But that's still not the main feature that makes me like Elm so much.

Balance

My career has always revolved around object-oriented languages (mostly Java). But around 2017 I started venturing into the world of Functional Programming.

When I started reading about this universe, a language that appeared recurrently was Haskell. It was a sort of holy place where people who really understood the subject would end up. Dazzled by the possibility of finding my holy grail, I started to venture into this language. Early on I realized that the path would be more tortuous than I imagined. Between comings and goings, I read some books and took some courses. But the learning curve was steeper than I expected!

I tried many different approaches, from trying to learn a more pragmatic framework like IHP (a kind of "Ruby on Rails" of the Haskell world), to very theoretical books and courses about Category Theory and Lambda Calculus. I would like to say that I found a way to significantly shorten the learning curve and became fluent in this language, but I would be lying through my teeth.

Even though I couldn't get too deep into this language, I managed to see a light at the end of the tunnel. A more colorful and happy world! But without a lot of help, I couldn't get there. It was frustrating, but I decided to postpone this journey. Another issue was that even if I got to the other side of that long path, I would hardly be able to bring many people with me. It would be like convincing my friends to do a multi-day and super difficult trail, promising that at the end they would see a beautiful waterfall. I found few people willing to face this journey with me.

Two people walking alone in the snow

Furthermore, Haskell was created within the academic world and, although today it is used in the most diverse areas, it is still quite common to find discussions within this community that, although interesting, are difficult to understand. There are exceptions, such as the Simple Haskell initiative. But I think it's safe to say that overall it's a difficult community to get into.

And in the middle of this path I came across Clojure: a functional language from the LISP family, much simpler and easier to learn. In a short time I was able to understand it's philosophy and was already using it in practice in a real system at work! I even created an Introduction to Functional Programming course (in Brazilian Portuguese) using this language as a base. But, although I like it a lot and want to learn more about it, I miss some features that only a strongly typed and pure language could provide.

It was along this journey that I came across Elm. Until then, I wasn't very interested in front-end. My career has been very back-end oriented. But I saw there an opportunity to flatten that learning curve. Since Elm is a domain specific language (webapp development), it is much simpler than Haskell! I decided to give it a chance as a way to prepare myself for facing Haskell later.

As I got to know the Elm community, I realized that this was a very good choice. The creator of the language has a very solid background in Haskell (the Elm compiler is written in Haskell) and makes it clear that his goal is precisely to bring the benefits he found in that language to more people. But that wouldn't be possible if he brought along all of Haskell's baggage and complexity.

When I say complexity, I'm not just referring to the more technical parts of the language, but also everything around it. You won't find people from the Elm community saying that to understand Elm you first need to study about Monads, Functors, Type Classes and many other topics that are frequently discussed in the Haskell community. And not because the community does not see value in it, but because they understand that it is possible to find another approach where, through simpler language, be able to transmit the ideas behind these concepts to more people. Later, if they wish, they can go deeper into these themes and find out for themselves what it all means. But to develop a good webapp, it is not necessary to understand the details of a Monad.

This balance between bringing from Haskell what really provides value, and avoiding those parts that are too complex (and with little added value - at least within the context of webapps development), makes us have a very clean, well-designed language with a much less steep learning curve.

Finding this balance is a difficult task. Advanced developers, already familiar with complex tricks and that are seeking a higher level of abstraction, may get frustrated and consider that Elm lacks fundamental tools. On the other hand, beginners in the world of functional programming, who are still not used to an expression-oriented programming language, may suffer when discovering, for for example, that every if needs an else.

Finally, we come to the reason that I believe is the main reason why Elm is such a delightful programming language: it manages to find (at least according to my criteria) a great balance in all of this.

And if you give it a chance and read the Elm introduction guide, maybe you'll also fall in love with this paradigm and never see programming with the same eyes again !

Bonus - Elm Fullstack?

I don't know if I'll ever be able to understand Haskell in depth and finally be able to reach that beautiful waterfall I was promised. But who knows, maybe I don't even need to follow this path? Evan has hinted that he is working on bringing Elm to the backend as well! On May 22, 2023 he will make the first public presentation of his work.

So this might be the best time in history to learn Elm! If you're as excited as me, I recommend (again) starting with the beginner's guide. Then you can continue with the book Elm in Action or, if you prefer an online course, this Udemy course by Carlos Saltos or this course by Richard Feldman.


Did you like this text? Checkout my other articles at: https://segunda.tech/tags/english and follow me on twitter.

. . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player