Things I learned writing my first Elm app
Tags: elm, functional, frontend
Published: 2017-11-26 15:40:24

---
title: "Things I learned writing my first Elm app"
published: true
description: A self-taught programmer's journey into functional frontend
tags: elm, functional, frontend
---
Some disclaimers up front. I am not a professional developer; I'm a self-taught programmer who's found himself in the unlikely position to be the Director of Technology for a school. Possessed of these skills, it's often the case that I see value in building a solution to our problems rather than purchasing one. To do this means successfully passing a rigorous cost-benefit analysis in which "build" beats "buy." It also means I have to build in such a way that I am not the only person who can maintain applications that become mission-critical. 

I'd like to tell you that I therefore follow every best practice: thorough documentation, writing tests for everything. I'm not there yet. I'm still learning.

About a year ago I learned about [Elm](https://elm-lang.org). Having dabbled in Haskell and written a React Redux app, I was completely enamored with functional programming and [The Elm Architecture](https://guide.elm-lang.org/architecture/), in particular. So when the opportunity to rewrite an in-house scheduling app came about, I knew I wanted to use Elm. It wasn't a particularly complex application, just a way to create recurring events on a six-day rotation rather than daily, weekly, or monthly. The perfect size for a foray into a new language.

But hang on, what about that maintainability thing? Wouldn't introducing a _completely alien_ language be a nightmare for anyone besides me who had to look at the code?

That brings me to the first thing I learned.

## Elm makes some clean code
I am in love with the way Elm code reads. The `update` function takes a set of messages sent by the `view` and updates the `model` (state of the app/module). It looks like this:

```elm
update : Action -> Model -> (Model, Cmd Action)
update action model =
    case action of
        NoOp ->
            model ! []

        RequestCalendars ->
            model ! [ getCalendarList clientId ]

        ReceiveCalendars calendars ->
            { model | calendars = calendars } ! [ getLetterDays letterDayCalendar ] 

        RequestLetterDayEvents ->
            model ! [ getLetterDays letterDayCalendar ]
```

If you've never read a functional language, that might look completely bananas. The first thing to know is that the top line is the function definition. It tells us what types come in to the function, and what type comes out. Then we name the arguments in the second line, which is equivalent to `function update(action, model){`. 

The `action` argument is of a special type that I've described myself. It describes the kinds of messages the view (what users see/interact with) can send to `update`. Beneath that, you see a `case/of` statement, which is similar to a `switch` statement in JS. Each line after an `->` indicates what `update` should return in the case of that given `action`.

`Action`, in fact, is part of the second thing I learned.

## I love strict typing
My programming journey began in Python, then moved to PHP, then JavaScript. So for a long time I had a bit of apprehension about static typing, to say nothing of strict typing. Then I learned Haskell, and discovered a new way to think about programming. Procedural languages treat programs as sets of instructions to execute in order—the classic "recipe" example. And that's all fine, and is often the absolutely correct way to conceive of a problem space. By contrast, functional languages like Haskell and Elm ask the programmer to consider the "shape" of information coming into a function and the shape that should emerge from a function. Applying the right operations to the information between input and output produces the desired result. Rather than thinking of steps in order, you begin thinking of enclosed transformations. In doing so, you _must_ be completely clear on your data types between input and output. That's where strict typing comes in. By defining functions first in terms of what comes in and goes out, there's never any ambiguity about what's happening or what's being passed around your code.

Consider the following mega-contrived example:

Suppose I wanted to write a function that takes in an array of numbers and returns them squared. Easy enough, right? Here is such a function in JS:

```javascript
function squareEach(nums) {
    let res = [];
    for (const n of nums) {
        res.push(n * n);
    }
    return res;
}
```

Okay, I know there's a cleaner way. Here you go:

```javascript
function squareEach(nums) {
    return nums.map( n => {
        return n * n;
    });
}
```

Problem is, I could only know `nums`'s type through comments. And while comments are good, nothing in comments will stop me from writing a really bad bit of code that ends up sending `undefined` or something worse to `squareEach()`.

Now, here's the same function in Elm:

```elm
squareEach : List Int -> List Int
squareEach nums =
    List.map (\n -> n * n) nums
```

First I define `squareEach`, telling Elm (and anyone reading my code) that it will take a list of integers and return the same kind of thing. Then I implement it, using a lambda (anonymous function) and `List.map` to emulate the same behavior as `Array.map()` in the second JavaScript example.

Now suppose I write some function elsewhere that tries to pass, say, a `String` to `squareEach`. The Elm compiler will catch it and provide an error for me. I can't even _compile_ stupid type mistakes.

And that's thing #3 I learned.

## Love recompiling
I fought with the Elm compiler a lot. Some mornings, I just had to stand up and walk away from the computer, since I could not get the compiler to agree to build my code. Invariably, my thinking was in error and taking the time to correct my mistakes the proper way resulted in cleaner, more elegant code. Spending copious amounts of time reading documentation helped. It isn't a great feeling, constant screens of red text in my terminal as Elm code fails. But you know what's an _incredible_ feeling?

No runtime errors. 

That's the tradeoff: rather than finding a ton of errors in production because an unexpected value slipped passed me, even with tests, hammering away against compiler errors results in a fairly stable application. No surprise `undefined`s to wreck your runtime. This isn't to say that Elm makes failproof apps—any language is only as good as its user. However, the number of issues caught by the compiler vastly reduces live debugging. Adding good tests to this mix is an even greater guarantee of stability. 

Well, I say "stable," but...

## Elm ain't done yet
There's no question this language is going places, and I've never had more fun putting together a frontend. Nevertheless, there are a few pieces still missing from the `core` library, like reasonable `Date/Time` tools. I used Justin Mimb's excellent [date-extra](http://package.elm-lang.org/packages/justinmimbs/elm-date-extra/) package to handle date/time parsing. But I shouldn't have to install a third-party dependency for ISO Strings, or to compare (`<`, `>`) `Date`s.

On the flip-side, the community packages are of a high quality. Just know that if you're making a non-trivial app with Elm, you'll end up installing a couple of third-party dependencies, or writing a bunch of your own helper code.

So if you want to change the way you think about programming, I highly recommend checking out a functional language. And if you want to fall in love with frontend again, I highly recommend Elm.