# How I Reduced My Code Size by 800%

**URL:** <https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092>\
**Category:** Show and Tell\
**Created:** [January 28, 2020, 5:36am UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092 "2020-01-28T05:36:27Z")\
**Posts on this page:** 13\
**Page:** 1

<div class="post-metadata">

**Author:** ![Jayshua](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jayshua/32/2707_2.png) [@Jayshua](https://discourse.elm-lang.org/u/Jayshua)\
**Post date:** [January 28, 2020, 5:36am UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/1 "2020-01-28T05:36:27Z")

</div>

TL;DR - I eliminated a bunch of update code by moving it into the view. It was a good idea. Trust me.

I have an application that manipulates lists quite a bit. The user can add things at the beginning, end, move things around, etc. Each item in the list is one of a few different element types, each with their own set of input fields. I implemented this a long time ago, when I was very new to Elm, in the naive and obvious way - msg types for every field of each element type, and msgs for all the list manipulation operations. There were a lot of them.

This was a painful file of code that was very difficult to understand if only for its sheer size. Something like 700 lines of case statements doing record updates and handing Maybe cases that could never happen. (Like the `UpdateElement Int UpdateElementMsg` which needed to account for the element at the given index not existing, or not being the type the UpdateElementMsg was for - neither of which would ever actually happen.)

So I tried to fix it now that I have much more experience with Elm. I ended up deflating 700 lines to around 80. The code for the entire list editing webpage nearly fits in my editor’s minimap now, which is neat. I figured I share the two techniques I used - maybe they’ll help someone else.

The first thing was to switch the view functions for each element type from `viewElement : Element -> Html Msg` to `viewElement : Element -> Html Element`. This added a few `\input -> { element | text = input }` lines to each element view function, but eliminated a **ton** of Msg types and update case statements.

So that cleared up most of the Element updating code, but the list manipulation code was still an extra-large plate of spaghetti. So I wrote this function:

```elm
viewList :
    { onSet : List data -> msg
    , viewItem :
        { item : data
        , set : data -> msg
        , delete : msg
        , insertAfter : data -> msg
        , insertBefore : data -> msg
        , moveBackward : msg
        , moveForward : msg
        }
        -> viewItemResult
    , viewList :
        { items : List viewItemResult
        , append : data -> msg
        , prepend : data -> msg
        }
        -> viewResult
    }
    -> List data
    -> viewResult
viewList config list =
    config.viewList
        { append = List.singleton >> (++) list >> config.onSet
        , prepend = (::) >> (|>) list >> config.onSet
        , items =
            List.indexedMap
                (\index item ->
                    config.viewItem
                        { item = item
                        , set = List.Extra.setAt index >> (|>) list >> config.onSet
                        , insertAfter = insertAt (index + 1) >> (|>) list >> config.onSet
                        , insertBefore = insertAt (index + 1) >> (|>) list >> config.onSet
                        , delete = config.onSet (List.Extra.removeAt index list)
                        , moveBackward = config.onSet (List.Extra.swapAt index (index - 1) list)
                        , moveForward = config.onSet (List.Extra.swapAt index (index + 1) list)
                        }
                )
                list
        }

```

That function does in 18 lines what used to take about 200. (I know what you’re thinking. “`(::) >> (|>) list >> config.onSet`. Really?” I swear it’s totally easy to read after understanding the basic pattern of `>> (|>) x`. Replace it with a lambda if you want.)

Now I can write the list view function like this (where the `viewUnit` function has the type `Unit -> Html Unit`):

```elm
type Unit
    = Text { content : String }
    | Audio { url : String }
    | Video { url : String }

view =
    viewList
        { onSet = SetList
        , viewItem =
            \{item, moveBackward, moveForward, delete, set} ->
                Element.tile
                    { header =
                        Element.horizontal
                            [ Element.upChevron moveBackward
                            , Element.downChevron moveForward
                            , Element.deleteIcon delete
                            ]
                    , body =
                        [ Html.map set (viewUnit item)
                        , ... some controls for inserting elements above and below ....
                        ]
                    }
        , viewList =
            \{ items, append } ->
                Element.vertical (
                    items
                    ++
                    [ Element.button "Add Text" (append (Text { content = "" }))
                    , Element.button "Add Audio" (append (Audio { url = "" }))
                    , Element.button "Add Video" (append (Video { url = "" }))
                    ]
                )
        }

```

My main takeaway from this exercise is “Msg types can be whatever the heck you want them to be, and probably should be.” (Obligatory incantation to keep the best-practice gnomes happy: Except functions. Never put functions in your messages.)

P.S. I might have code-golfed a bit too much with that `viewList` function. At least it was fun to write.

---

<div class="post-metadata">

**Author:** ![edkelly303](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/edkelly303/32/1510_2.png) [@edkelly303](https://discourse.elm-lang.org/u/edkelly303)\
**Post date:** [January 28, 2020, 9:46am UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/2 "2020-01-28T09:46:27Z")

</div>

Interesting! Can you show what your `update` function looks like now, too?

---

<div class="post-metadata">

**Author:** ![gyzerok](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/gyzerok/32/898_2.png) [@gyzerok](https://discourse.elm-lang.org/u/gyzerok)\
**Post date:** [January 28, 2020, 11:14am UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/3 "2020-01-28T11:14:27Z")

</div>

As far as I remember some time ago there were chances of getting race conditions, if you update your model in your view. Not sure if it still applies, but I’d personally double check 🙂

---

<div class="post-metadata">

**Author:** ![Herteby](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/herteby/32/1329_2.png) [@Herteby](https://discourse.elm-lang.org/u/Herteby)\
**Post date:** [January 28, 2020, 12:20pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/4 "2020-01-28T12:20:23Z")

</div>

Yes `viewElement : Element -> Html Element` can lead to race conditions.  
If you for example have both an `onBlur` and an `onClick` event which fire at the same time, and one returns `{element | foo = True}` and the other returns `{element | bar = True}`, then one will overwrite the other, so that only either `foo` or `bar` will be `True`.

I think to avoid that you could make it `viewElement : Element -> Html (Element -> Element)` instead. So that the message is a function, which gets supplied with the element in the update function.

---

<div class="post-metadata">

**Author:** ![Herteby](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/herteby/32/1329_2.png) [@Herteby](https://discourse.elm-lang.org/u/Herteby)\
**Post date:** [January 28, 2020, 12:38pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/5 "2020-01-28T12:38:44Z")

</div>

Actually it rarely seems to happen with `onBlur` + `onClick`, unless you have a supersonic finger, because `onBlur` fires when you press the button, and `onClick` fires when you release it.

Here’s a repro with `onBlur` + `onMouseDown` though:  
[https://ellie-app.com/7SP32mLcqFZa1](https://ellie-app.com/7SP32mLcqFZa1)  
Click on the input field, then click on the button. What you might expect is for both `foo` and `bar` to change, but only one of them does.

(`onBlur` + `onMouseDown`) is just one example, this could potentially happen with any event.

---

<div class="post-metadata">

**Author:** ![pdamoc](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/pdamoc/32/36_2.png) [@pdamoc](https://discourse.elm-lang.org/u/pdamoc)\
**Post date:** [January 28, 2020, 1:20pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/6 "2020-01-28T13:20:34Z")

</div>

> [@Herteby](#):
>
> I think to avoid that you could make it `viewElement : Element -> Html (Element -> Element)` instead.

This goes agains the recommendation against sending functions in messages.

> [@Herteby](#):
>
> ( `onBlur` + `onMouseDown` ) is just one example, this could potentially happen with any event.

I would be curious what other events can trigger this behavior when combined (outside of focus events).

---

<div class="post-metadata">

**Author:** ![Jayshua](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jayshua/32/2707_2.png) [@Jayshua](https://discourse.elm-lang.org/u/Jayshua)\
**Post date:** [January 28, 2020, 3:16pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/7 "2020-01-28T15:16:04Z")

</div>

Basically just this:

```elm
update msg model =
    case msg of
        SetList newList ->
            ({model | list = newList}, Cmd.none)
        ...

```

---

<div class="post-metadata">

**Author:** ![Jayshua](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jayshua/32/2707_2.png) [@Jayshua](https://discourse.elm-lang.org/u/Jayshua)\
**Post date:** [January 28, 2020, 3:26pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/8 "2020-01-28T15:26:35Z")

</div>

This is why I limit the use of `Html Element`. There are lots of other msgs that the various parts of the page can produce - but the `Html Element` type is kept to as small of a portion of the view as I can.

Limiting the use of `Html Element` is also one of the reasons the `viewList` function does the weird twist-around thing with the msg types to create a `data -> msg` function to pass to the provided view - so that the function you give it can produce `Html Msg` instead of `Html Element` - that way you can produce all the other more custom Msg types and keep the `Html Element` as targeted as possible, reducing the chance of race conditions.

In this case the only events that produce `Html Element` are `onInput` for input fields and `onClick` for checkbox fields. So no chance of race conditions as far as I can tell.

It’s also enforced with my form library which looks like this:

```elm
Form.view
    [ Form.text "Video Title" Focus.title
    , Form.errors [(TitleEmpty, "The title is required")]
    , Form.text "Video URL" Focus.url
    , Form.errors [(UrlEmpty, "The URL is required"), (UrlInvalid, "That doesn't look like a URL!")]
    ]

```

If you stick to the `Form.view` function, race conditions like that can’t happen because it only attaches `onInput` handlers to the dom.

---

<div class="post-metadata">

**Author:** ![joshuakb2](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joshuakb2/32/2811_2.png) [@joshuakb2](https://discourse.elm-lang.org/u/joshuakb2)\
**Post date:** [January 28, 2020, 9:29pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/9 "2020-01-28T21:29:09Z")

</div>

I’ll admit that I’ve never seen `f >> (|>) x` before, and it wasn’t immediately obvious to me what it means, but eventually I worked out that it’s equivalent to `flip f x` where `flip` exchanges the first and second arguments of `f` (`flip : (a -> b -> c) -> (b -> a -> c)`). So in this case,

`(::) : a -> List a -> List a`  
and  
`(::) >> (|>) list` = `flip (::) list` = `(\item -> item :: list)`

I think I find `flip (::) list` to be a good compromise between clarity and conciseness.

---

<div class="post-metadata">

**Author:** ![Jayshua](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/jayshua/32/2707_2.png) [@Jayshua](https://discourse.elm-lang.org/u/Jayshua)\
**Post date:** [January 28, 2020, 11:38pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/10 "2020-01-28T23:38:02Z")

</div>

My thought process went: “I’d just use flip here. But Elm doesn’t have flip built-in anymore, does it? Guess I could just use a lambda, or write flip myself. The value will be a function without the flip, right? Can I just apply it in the pipeline? Oh, I can. Neat. I probably shouldn’t write this.” I then proceeded to write it anyway.

---

<div class="post-metadata">

**Author:** ![joshuakb2](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joshuakb2/32/2811_2.png) [@joshuakb2](https://discourse.elm-lang.org/u/joshuakb2)\
**Post date:** [January 29, 2020, 12:45am UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/11 "2020-01-29T00:45:19Z")

</div>

My first exposure to any kind of functional programming was JavaScript’s array functions map(), filter(), and reduce(). I started using them almost any time I could, chaining them together and usually using lambdas. Looking at my code from back then, you’d think I was trying to see how much I could accomplish in a single statement, to the detriment of readability.

These days, I’m trying to pay more attention to whether the code is clear to the reader, even though the expressiveness afforded by functional programming styles can be addicting!

---

<div class="post-metadata">

**Author:** ![rofrol](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rofrol/32/60_2.png) [@rofrol](https://discourse.elm-lang.org/u/rofrol)\
**Post date:** [February 1, 2020, 9:54pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/12 "2020-02-01T21:54:50Z")

</div>

I think writing yourself `flip` or importing from basics-extra will be much clearer

> <https://github.com/elm-community/basics-extra/blob/360e0a0fe9c6c041bcde5e0d62300f0a7928b626/src/Basics/Extra.elm#L145>

---

<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:** [February 11, 2020, 9:54pm UTC](https://discourse.elm-lang.org/t/how-i-reduced-my-code-size-by-800/5092/13 "2020-02-11T21:54:54Z")

</div>

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