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 variable 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 data types, such as
List, that have already an empty value, the type
Maybe List offers two blank states:
. 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. In fact TEA, The Elm Architecture, inspired frameworks such as Redux. TEA 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
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.
Finally, Richard Feldman, author of elm-spa-example, demonstrates different refactoring techniques that come handy as your code base grows. He discusses narrowing responsibilities on various levels.
Thanks to the Elm language server, should you use Emacs or VSCode, you get a bunch of IDE functions. However the biggest aid is still the language and the Elm compiler itself. Thanks to that, it’s possible to have tools such as the elm-review that can remove dead code automatically.
There are many great resources available to study Elm.
- Beginning Elm: a gentle introduction to Elm programming language, a book by Pawan Poudel, is freely available online
- Programming Elm: Build Safe and Maintainable Front-End Applications, a book by Jeremy Fairbank, for those who prefer the classic format
- Elm Weekly, a newsletter by Alex Korban, a source for the news from the Elm ecosystem and further study materials as well.
- Elm Patterns, a collection of common patterns for Elm
The Zen of Elm
Inspired by The Zen of Python, I compiled a verse of patterns and best practices in Elm.
- Narrow is better than broad.
- Modules should be built around a central type.
- Parse, Don’t Validate.
- Wrap early, unwrap late.
- Opaque is better than transparent.
- Maybe is better than blank.
- Although a custom type might be better than nothing.
- Make impossible states impossible.
- Unhandled cases should never pass silently.
- There should be one – and preferably only one – obvious way to write it.
- Always prefer qualified names.
- Use custom types for record IDs.
- Data structure should be the last argument of a function.