# Use key, get\_key and modify to reduce boilerplate in update

**URL:** <https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103>\
**Category:** Request Feedback\
**Created:** [March 15, 2021, 2:54pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103 "2021-03-15T14:54:01Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![jfine2358](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jfine2358/32/3875_2.png) [@jfine2358](https://discourse.elm-lang.org/u/jfine2358)\
**Post date:** [March 15, 2021, 2:54pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/1 "2021-03-15T14:54:01Z")

</div>

(0) I want to reduce boilerplate code in the Elm files I write. I’m happy to put the boilerplate elsewhere, particularly if it’s in a file that can be automatically generated from the rest of the project.

I’m looking for references to prior art, alternative approaches, and also feedback. I’d be delighted if someone else has already discovered and implemented this idea. That would mean less work for me!

(1) My toy problem is to create a selector for RGB values. So I start my app with

```elm
type Msg = R | B | G
init = { red = 0, blue = 0, green = 0 }

```

You can see the whole implementation on Ellie [https://ellie-app.com/cDdvYdnJXkca1](https://ellie-app.com/cDdvYdnJXkca1).

(2) Here’s my `update` method.

```elm
update msg state =
    let
        key = get_key msg
        prev = key state
        next = prev + 1
    in
    modify state key next

```

There’s no `case` in `update`. That’s hidden in `get_key`. We use `modify` to create the new `state`.

(3) All this can be made to work with Elm 0.19. Here’s some crucial parts of the implementation.

```elm
get_key msg =
    case msg of
        R -> .red
        B -> .blue
        G -> .green

lookup =
    { red = \record value -> { record | red = value }
    , blue = \record value -> { record | blue = value }
    , green = \record value -> { record | green = value }
    }

modify record key value =
    key lookup record value

```

(4) Much of (3) can be generated automatically. In particular, `lookup` needs only a list of all record keys in the app. And perhaps the `get_key` function for `Msg` can be similarly generated.

---

<div class="post-metadata">

**Author:** ![jfmengels](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jfmengels/32/3124_2.png) [@jfmengels](https://discourse.elm-lang.org/u/jfmengels)\
**Post date:** [March 15, 2021, 3:58pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/2 "2021-03-15T15:58:21Z")

</div>

Hi @jfine2358

I find the approach interesting. That said, I don’t think it’s a particularly practical solution. (So this is more of a feedback comment, I don’t have much to bring on prior art or alternative approaches)

First of all, you mention you want to remove boilerplate. But the solution is not particularly terser.  
I rewrote your program the way I’d do it here: [https://ellie-app.com/cDfb48KRyKma1](https://ellie-app.com/cDfb48KRyKma1)  
The total amounts to 49 LOC (I removed comments and formatted using `elm-format`).  
In comparison, yours (with the same changes) is at 58 LOC without counting the functions you showed in (3), 81 with ([https://ellie-app.com/cDfmMKHdkCCa1](https://ellie-app.com/cDfmMKHdkCCa1)).

Your solution does have the benefit of making it very hard to mix up colors (`{ state | red = state.green + 1 }` for instance). Also, if you have 100 colors, you program might be a bit terser because of the way that `elm-format` does formatting (no line-break in records).

Side-note: if you do have hundreds of colors, then you’ll also store an object (two actually) with 100 functions in memory. For simple things like these, that might be okay, but for very complex things you’ll need more data.

My second and bigger issue my worries is about how you’d extend the solution. Let’s say you want to add a reset button that resets all buttons. My feeling is that you’d have to either go through a lot of hoops in order to make your program still work in order not to change the update function, or do a case statement inside your update function where in the reset case you’d reset all values to 0, and in the other you’d do what you currently do (basically wrapping your current approach).

I think your approach works, but only so because you have such a special and uniform program. What if you had the same program, but for `R` you’d also have a reset button, for `G` you’d have a different text and for `B` you’d decrement the value? You’d probably do so by adding new records or new fields go your “configuration” records. In practice multiplying the data you’d have in memory (as I mentioned before).

In my more conventional approach, it would be reasonably trivial to handle all those cases.

I find that boilerplate brings flexibility, and it’s not something to necessarily fight against. Obviously, when some logic is very uniform, it might be interesting to add abstractions like those. But beware of premature abstraction and making your program harder to iterate on.

---

<div class="post-metadata">

**Author:** ![jfine2358](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jfine2358/32/3875_2.png) [@jfine2358](https://discourse.elm-lang.org/u/jfine2358)\
**Post date:** [March 15, 2021, 6:29pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/3 "2021-03-15T18:29:24Z")

</div>

(0) Thank you @jfmengels for your interest and thoughtful comments. I’m grateful for your providing a more traditional solution, and doing a fair-minded comparison. You’ve raised many important issues. In this response I’ll focus on the size penalty of my approach. First I consider LOC. Then I consider asset size and memory use.

Later this week I’ll respond to your potentially fatal concern

> My second and bigger issue my worries is about how you’d extend the solution

(5) (Here, all LOC counts are after `elm-format`.) My version has 58 LOC (excluding what I call the boilerplate). Your traditional solution has 49 LOC.

But don’t put too much weight on that comparison. To reduce LOC, I’ve replaced

```elm
update msg state =
    let
        key = get_key msg
        prev = key state
        next = prev + 1
    in
    modify state key next

```

by

```elm
update msg state =
    modify state (get_key msg) (get_key msg state + 1)

```

This reduces my excluding boilerplate LOC to 48. See [https://ellie-app.com/cDhkX6QMZFwa1](https://ellie-app.com/cDhkX6QMZFwa1).

(6) You wrote:

> [@jfmengels](#):
>
> Side-note: if you do have hundreds of colors, then you’ll also store an object (two actually) with 100 functions in memory. For simple things like these, that might be okay, but for very complex things you’ll need more data.

The final size penalty depends on the optimisations the Elm compiler provides. According to [Small Assets without the Headache](https://elm-lang.org/news/small-assets-without-the-headache), Elm 0.19 does function-level dead code elimination. It also provides record field renaming.

The Haskell wiki [states](https://wiki.haskell.org/Performance/GHC#Modules_and_separate_compilation)

> In general, splitting code across modules should not make programs less efficient. GHC does quite aggressive cross-module inlining: when you import a function f from another module M, GHC consults the “interface file” M.hi to get f’s definition.

(7) Assuming the Elm compiler does similar optimisations, I’d expect your solution and mine to produce similar values after suitable optimisation. But this expectation is based on ignorance, and not informed by experience.

---

<div class="post-metadata">

**Author:** ![solificati](https://avatars.discourse-cdn.com/v4/letter/s/2bfe46/32.png) [@solificati](https://discourse.elm-lang.org/u/solificati)\
**Post date:** [March 15, 2021, 6:33pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/4 "2021-03-15T18:33:31Z")

</div>

> [@jfine2358](#):
>
> I’m looking for references to prior art

You’re reinventing lenses 🙂

---

<div class="post-metadata">

**Author:** ![jfine2358](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jfine2358/32/3875_2.png) [@jfine2358](https://discourse.elm-lang.org/u/jfine2358)\
**Post date:** [March 15, 2021, 7:48pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/5 "2021-03-15T19:48:53Z")

</div>

(0) Thank you for this @solificati. There’s some overlap, but I think I’m adding something that’s new and potentially useful.

Here’s a longish post to clarify. But in short, what I see that that both my idea and `Moncocle.Lens` use `get` and `set` functions. But I don’t see any other major similarities. I think that if you recode my RGB example using `Monocle.Lens` you’ll see the differences are important.

Joke: There’s some a nice piece of fruit, or an electronic gadget, waiting when you get to then end of this post.

(8) In [Monocle.Lens](https://package.elm-lang.org/packages/arturopala/elm-monocle/latest/Monocle.Lens) I read:

```elm
type alias Lens a b =
    { get : a -> b
    , set : b -> a -> a
    }

```

So, not tested, the two statements below are equivalent and work.

```elm
red_lens = Lens .red \record value -> { record | red = value }

```

```elm
red_lens = Lens .red lookup.red

```

(9) The overlap is having `get` and `set` methods.

(10) The differences I see are

1. `Lens` is considerable more general and abstract. My method uses only field access functions such as `.red`, `.blue` and more generally `.name`.

2. The `.name` field access functions are already readily available.

3. I suggest providing systematic ready access to the corresponding `get` functions.

4. These `get` functions are already in wide use, but as literals rather than functions. The following are intended to be equivalent.

```elm
new_state = { prev_state | name = value } -- Literal.

```

```elm
new_state = modify state .name value -- Argument.

```

1. In the above `.name` can be replaced by a parameter, such as `key`. This allows code branches to be combined. This can’t be done using the literal form.

```elm
new_state = modify state key value

```

1. The system is to code the `get` functions for you. They’ll be guaranteed to be correct.

(11) In short, what I see is that both my idea and `Lens` use `get` and `set` functions. But I don’t see any other major similarities. I did read about `Lens` before writing my code. I didn’t understand it at all well, but I could see that it was unlikely to do what I wanted.

(infinity) By the way, here’s something that’s hardish to read. What color is this lens?

```elm
blue_lens = something

```

Related is fooling AI into thinking that an edible apple is an iPod electronic gadget.

> **[OpenAI’s state-of-the-art machine vision AI is fooled by handwritten notes](https://www.theverge.com/2021/3/8/22319173/openai-machine-vision-adversarial-typographic-attacka-clip-multimodal-neuron)**
>
> Reading is believing.

---

<div class="post-metadata">

**Author:** ![solificati](https://avatars.discourse-cdn.com/v4/letter/s/2bfe46/32.png) [@solificati](https://discourse.elm-lang.org/u/solificati)\
**Post date:** [March 15, 2021, 9:03pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/6 "2021-03-15T21:03:14Z")

</div>

> [@jfine2358](#):
>
> Thank you for this @solificati. There’s some overlap, but I think I’m adding something that’s new and potentially useful.

I was thinking about more canonical definition of lenses from Haskell. You asked about prior art. In Haskell there is no nice record syntax so they also generate lenses via TemplateHaskell.

I assume Monocle is rather underdeveloped due to Elders of Elm not liking concept of lenses.

---

<div class="post-metadata">

**Author:** ![jfmengels](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jfmengels/32/3124_2.png) [@jfmengels](https://discourse.elm-lang.org/u/jfmengels)\
**Post date:** [March 16, 2021, 10:19am UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/7 "2021-03-16T10:19:05Z")

</div>

> [@jfine2358](#):
>
> […] But don’t put too much weight on that comparison. To reduce LOC, I’ve replaced […]

I totally agree that lines of code (or even number of characters) is not an important metric, especially when you have a compiler helping you prevent a lot of errors that can potentially happen and that [the size of a program does not lead to more errors](https://www.youtube.com/watch?v=XpDsk374LDE).

I guess it is important to wonder “what” boilerplate is though, from the point of view of someone who wants to reduce it. For some it is indeed about terser code, which makes sense because terser code _can_ lead to easier to understand programs.

For me, the biggest potential problem with boilerplate is forgetting to connect elements (forgetting to call the update function or [forgetting to call the subscriptions function](https://package.elm-lang.org/packages/jfmengels/elm-review-the-elm-architecture/latest/NoMissingSubscriptionsCall)) or misconnecting elements (doing `{ state | red = state.green + 1 }` for instance).

In my experience, I find that the problem of misconnecting elements is very rare, even with large refactors. Forgetting to connect things is more common but I found the [`elm-review-unused`](https://package.elm-lang.org/packages/jfmengels/elm-review-unused/latest/) to be quite good at detecting that (it won’t detect everything, but I know it has helped prevent people from shipping bugs to production already).

To me, boilerplate is simply connection logic, and while they feel like it could or should be omitted sometimes, it does add a lot of flexibility to how you can connect things.

> [@jfine2358](#):
>
> The final size penalty depends on the optimisations the Elm compiler provides. According to [Small Assets without the Headache](https://elm-lang.org/news/small-assets-without-the-headache), Elm 0.19 does function-level dead code elimination. It also provides record field renaming.

Right. So for the record field renaming, it may indeed replace `{ red = ..., blue = ..., green = ... }` by something shorter like `{ r = ..., b = ..., gz = ... }`. So probably not a great gain, but something still.

As for function-level dead code elimination, it won’t apply too much in our cases. If for some reason, we never display the blue button, the Elm compiler is not smart enough to notice that the `blue` field can be removed everywhere (or even anywhere).

The difference in our solutions is that mine will potentially not evaluate some code until necessary (with the downside that those things will likely be re-evaluated every time it is needed) whereas with your solution those things will be evaluated at start-up time (slowing down the startup) and kept in memory (increasing memory usage), with the upside that it might be faster to re-use on every need.

These last differences will unlikely be noticed, but I think that the more you use this approach in an Elm app (up to using it everywhere) and the bigger your app is, the longer the initial startup time will be and the more memory the app will take. Though as always with performance: Benchmark before you try evaluate things and I will admit I have not benchmarked any of this.

My main concern with such an approach (and with almost all solutions that aim to reduce boilerplate) is how much you lose in flexibility, because the solution against boilerplate is using abstractions, and abstractions have a potential cost. Everything depends, so go for these solutions when you’re pretty darn sure what you are working with can be abstracted as you have done, and that it will likely not change, then cross your fingers.

---

<div class="post-metadata">

**Author:** ![jfine2358](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jfine2358/32/3875_2.png) [@jfine2358](https://discourse.elm-lang.org/u/jfine2358)\
**Post date:** [March 22, 2021, 5:50pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/8 "2021-03-22T17:50:32Z")

</div>

This post suggests that **cable (or wire) harness** might be a better term that **boilerplate**.

> [@jfmengels](#):
>
> To me, boilerplate is **simply connection logic** , and while they feel like it could or should be omitted sometimes, it does add a lot of **flexibility** to how you can connect things. [Emphasis added]

According to wikipediate, [Boilerplate text](https://en.wikipedia.org/wiki/Boilerplate_text) is

> is any written text (copy) that can be reused in new contexts or applications without significant changes to the original. The term is used in reference to statements, contracts and computer code, and is used in the media to refer to hackneyed or unoriginal writing.

I don’t like the idea that a change to computer code isn’t significant. Perhaps there’s a better term that boilerplate. I suggest:

> **[Cable harness](https://en.wikipedia.org/wiki/Cable_harness)**
>
> A cable harness, also known as a wire harness, wiring harness, cable assembly, wiring assembly or wiring loom, is an assembly of electrical cables or wires which transmit signals or electrical power. The cables are bound together by a durable material such as rubber, vinyl, electrical tape, conduit, a weave of extruded string, or a combination thereof.
> Commonly used in automobiles, as well as construction machinery, cable harnesses provide several advantages over loose wires and cables. For exam...

We are used to the idea of a test harness. According to wikipedia, a cable harness has terminals that allow it to be connected to the electrical components, and often a cable harness is subject to electrical tests after manufacture.

> [@jfmengels](#):
>
> My main concern with such an approach (and with almost all solutions that aim to reduce boilerplate) is how much you lose in flexibility, because the solution against boilerplate is using abstractions, and abstractions have a potential cost.

This is a sound concern, which influenced me to think of cable (or wire) harness as a better analogy than boilerplate. It has an element of skill and creativity associated with it, that the term boilerplate does not. Sometimes the **connection logic** is not simple, particularly if there are problems in getting the types consistent.

---

<div class="post-metadata">

**Author:** ![wolfadex](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/wolfadex/32/2461_2.png) [@wolfadex](https://discourse.elm-lang.org/u/wolfadex)\
**Post date:** [March 22, 2021, 8:18pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/9 "2021-03-22T20:18:00Z")

</div>

The cable harness analogy resonates strongly with me as I spent my first 4 years out of public school building them for aircraft. There were multiple times where we attempted to automate, in essence, portions of the process but it was rare that it was possible or practical to do so.

> [@jfine2358](#):
>
> It has an element of skill and creativity associated with it, that the term boilerplate does not. Sometimes the **connection logic** is not simple, particularly if there are problems in getting the types consistent.

This in particular is so true. The most skilled of my coworkers at the time I always compared to artists because their final work was always one of a kind and immaculate.

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [April 1, 2021, 8:18pm UTC](https://discourse.elm-lang.org/t/use-key-get-key-and-modify-to-reduce-boilerplate-in-update/7103/10 "2021-04-01T20:18:27Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
