I have to admit that it took some time until I became friends with the syntax of the language. Eventually I find Elm’s syntax very clear and explicit, and now I love it. With help of elm-format, there’s even one preferrable way to write Elm code.
What came to me as a suprise was that partial application is not only a feature you may use like in other functional programming languages. Partial application is baked in how you define any functions in Elm. In fact, all functions in Elm take only argument.
It’s liberating how safe and fun is to do refactoring an Elm code base. Static typing combined with Maybe, pattern matching and Elm’s friendly compiler messages are incredibly helpful. Change the name of a constant or a type in a function signature, and the compiler will tell you exactly what you need to correct. Introduce a new value for a custom type and the compiler will let you know of unhandled cases. No more guard conditions to make sure you access the value you assumed. No more unit tests to make sure you handle invalid input cases. When refactoring becomes this easy, improving code design becomes habitual.
Although first I struggled a bit with the Maybe type, it’s a powerful tool for explicitly stating if a certain value can be missing. If there’s a chance of absent data, the compiler forces you to handle that case. No more Cannot read property ‘context’ of undefined errors in production. Interestingly, when it comes to handling lists, the type Maybe List offers two blank states: Nothing and . In this case, it’s recommended to use custom union types to be more expressive.
Type aliases provide an elegant way to reuse existing types and give them a more meaningful name in the given context. However, when it comes to record IDs, they are often best described as custom types. This way the compiler can make sure that only the right type of value can be passed around. Furthermore, opaque types also advocate an API that doesn’t expose implementation details.
Elm is not only a language, but also an architecture that was designed to build webapps with. Think of it as TypeScript, React and Redux bundled together. Yet, The Elm Architecture is simple, straight-forward with no hidden magic. Regardless of the complexity of the web app, it all boils down to a Model data record, an update and a view function. Therefore there’s only one way to build a web app with Elm.
Everyone carries code organization and refactoring best practices they picked up in the past. However, they aren’t necessarily meaningful in Elm. The official guide suggests to focus on finding the right types and data structure first to describe the problem, and worry about file size later. Organize your code around types, not components or the MVC pattern. As I mentioned before, in the end of the day what you need to provide is a single Model record and the update and view functions.
In his talk The life of a file, Evan Czaplicki shows his approach of figuring out when to split code. He illustrates the problem with two checklists that would give the idea for many of abstracting away a checkbox list component. While implementing the business logic, it turns out that eventually there’s very little in common between the two solutions and early abstraction would have lead to poorer code design.
Alex Korban, author of the book Practical Elm, also shares his take on code organization. He points out that “you are still ultimately passing a single update function and a single view function to Html.program” when working with the Elm architecture.
There are many great resources available to study Elm.