Elm, functional programming

As I continue to learn Elm, it seems the big payoff in functional programming happens during maintenance – the changes you make down the road. There is a learning curve, and writing the initial program is often harder than using an “easier” language like Javascript. However, when you go to make a change, and it is simple, you start to realize – this is pretty neat. Recently I wanted to sort the display of nodes in Simple IoT by description, so I made the following change:

Now I have a nicely sorted display:

In Javscript, this type of change would fill me with fear and trepidation. In Elm, was easy, and had zero worries about bugs or adverse effects. This is another example of the choice we all have – do we want to optimize for the short term or the long term.

1 Like

With use, I realized I put the sort in the wrong place – if nodes are sorted while displaying, then the order of nodes changes while you are editing them, which is kind of annoying. It would be better to sort only after the node edit is saved. This had me puzzled for a bit, but turns out the change was super simple:

The sortNodeTree is a recursive function which takes a Tree, sorts the root node’s children, then calls sortNodeTree on each child – in the end, very simple. Because data is immutable in Elm, you are forced to use recursive functions for something like this and in the end, the solution is much simpler than modifying a static Tree variable in place. Functional programming is amazing – often takes a little more work to think through a proper solution, but the end results is usually nice.

This is another example of the “big payoff … during maintenance” – this change was very simple (once I understood how to make it), and the sort logic could be reused without any modification.

I made another observation today – it seems Elm code gets cleaner with time rather than messier. As I’m adding new features, I often see areas that can be simplified and cleaned up – like this:

Perhaps this is not specific to functional programming, but seems to happen more for me than with other languages. One reason for this is the ease of refactoring – in C++, to change structure, you need to define new classes, change headers, etc – very high friction. In Elm, you can create a new function at any of the argument boundaries.

In the above example, the el function normally takes two arguments:

el : List (Attribute msg) -> Element msg -> Element msg

However, with the following code we use a partial application

alignButton: Element msg -> Element msg
alignButton =
            el [ alignTop, paddingEach { top = 10, right = 0, left = 0, bottom = 0 } ]

to create a new function that includes the attribute list, which is common to all its applications. Being able to create new functions at any argument boundary is very powerful and makes refactoring easy.

Here is another example where we partially apply a function to create a shorter version that gets used in this function and makes the resulting code much more concise and readable.

In the following episode, there is some discussion of how using Elm and Typescript compare. There is also some discussion about Rust.

Another thing @dillonkearns said on a recent podcast (can’t remember which one) is (I’m paraphrasing) a little boilerplate and extra typing is not the hard part – what is hard is finding tricky bugs in your code. Elm eliminates most debugging (once you get it to compile :slight_smile: ). With experience, you learn to take smaller steps so as to not upset the compiler too badly and make it easier to fix compile errors.

Another observation I’ve made recently: Not being able to create new components with local state easily seems pretty annoying initially – especially for those of us coming from React. In Elm, you end up having to pass in messages to functions in different modules as the following guide suggests:

This works pretty well in practice, and generally ends up being much simpler and easier to maintain that more state scattered throughout your program. I still use elm-spa to give me local state for each page – that seems to be a pretty good boundary for state, and elm-spa does all the heavy lifting there. Once again, Elm steers you toward good patterns – not sure if this was intentional or just worked out this way.

As the Simple IoT app becomes larger and I am doing more refactoring – Elm feels like an excellent choice for this project.

A quote from this discussion:

I wonder if maybe this is the wrong approach for these kinds of discussions. In general it’s not terribly difficult to build any particular project with any particular language, framework, typing style, etc. Sure Elm may not be suited to servers or clis, but it can be done. To me the more interesting question is how that code base holds up 5 or 10 years down the road? Is it still easy to build? Is it still easy to onboard? Is it still easy to add features? This is where most programming tools don’t focus from what I’ve experienced.

This indeed the important question for many decisions – what is the long term cost? Modern products are rarely ship and forget – they are built on flexible platforms where there is tons of headroom to add features/value in the future. Thus, those who build for the long term will have a much easier time.