Loading state design revisited in Elm
Tags: elm, architecture, webdev
Published: 2020-08-15 14:58:30

---
title: Loading state design revisited in Elm
published: true
description: How do you design loading state in your Elm application?
tags: elm, architecture, webdev
---

If you are a frontend dev, I suppose that you have been frequently through the scene that you implement loading view in the frontend application. 

Elm is a domain-specific language built for frontend applications, so it is pretty usual to design loading view. The great thing is that, Elm has `Maybe` that says if the value is available or not in a type level. Using `Maybe` in designing loading state in Elm is really naive pattern, but this is legit.

```elm
type alias Model =
    { user : Maybe User
    , bookmarks : Maybe Bookmarks
    }
```

Now, we can implement `init` function as follows. `Nothing` is a variant that says no value available, so we can understant that this function returns empty Model anyway.

```elm
init : Model
init =
    { user : Nothing
    , bookmarks : Nothing
    }
```

As I said this is a naive pattern, this Model design is failure in an Elm way.

First of all, `Maybe` does not carry any of context information. `Maybe` is just a type says it can be `Nothing` or `Just`, so it does not tell you why it is `Maybe`. This is meta, but the bigger your codebase gets, the more critical how to convey the context of it by code is. 

`Maybe` is way better than Boolean, but using it too many times is almost equal to Boolean Identity Crisis.

{% youtube 8Af1bh-BVY8 %}

# Unleash Custom Type
Let the code tell us its context. This is exactly what Elm as a typed language should be, and where Custom Type comes into.

```elm
type Model
    = None
    | UserLoaded User
    | BookmarksLoaded Bookmarks
    | AllLoaded User Bookmarks
```

Our Model is now way more descriptive than before. We can understand that bookmarks and an user are needed to be waited until fetched.

In addition to the descriptivity, this also has advantage that **we don't have to explicitly unwrap them out to non-Maybe types anymore!** If every single data waited to be fetched is all wrapped with `Maybe`, it bothers us that we always have to unwrap it every single time even at where we are sure that it would never be `Nothing`. We can now extract all data just by a single pattern matching of Model.

I have learnt this pattern from the video, "Make Data Structure" by Richard Feldman at Elm Europe 2018.

![A page of Richard's slide ](https://dev-to-uploads.s3.amazonaws.com/i/rjmrlunzj84yvkl7m02k.jpg)
[Make Data Structure by Richard Feldman](https://www.youtube.com/watch?v=x1FU3e0sT1I)

# Pitfall: Combinatorial Explosion
However, we would easily come across the case that designing Model simply with Custom Type does not actually work out.

Imagine that some more additional data sources are needed to be fetched in initialization. Working with two is still duable by designing your Model with a simple Custom Type like the previous, but how about more of it? The bigger the number gets like three, four, five, the harder it is to cover all their patterns up.

This is a kind of combinatorial explosion. If you design waiting state on your Model which waits 3 resources at the intialization.`Maybe` has cardinaliy of 2 which is the number of exponent of waiting resources, so the result is 3^2=9. Now we can see that the number of variants on your model is 9. It exceeds 9 if additional error handling is needed. 

**The number of variants can easily be exploded.** Using a simple Custom Type in desiging waiting state in your Model is totally unrealistic if it is more than two.

# elm-multi-waitable 
As one of the solution for the variant explosion in Model, I crafted a small package that encapsulate multiple `Maybe`s from Model. This package is well tested on our product built with Elm.

{% github IzumiSy/elm-multi-waitable no-readme %}

The great thing this package exactly improve is your Model design that needs waiting multiple resources. If your application has to wait 3 resources at the initialization, your Model will look like as follows.

```elm
type Model 
    = Loading (MultiWaitable.Wait3 Msg User Options Bookmarks)
    | Loaded User Options Bookmarks
```

Very neat! Even if we need more of resources waiting, the variants never be exploded anymore.

The rest that wires everything up is as follows.

```elm
init : Model
init =
    Loading <| MultiWaitable.init3 AllFinished


type Msg
    = AllFinished User Options Bookmarks
    | UserFetched User
    | OptionsFetched Options
    | BookmarksFetched Bookmarks


update : Model -> Msg -> ( Model, Cmd msg )
update model msg =
    case (model, msg) of
        ( Loading waitable, UserFetched user ) ->
            waitable
                |> MultiWaitable.wait3Update1 user
                |> Tuple.mapFirst Loading

        ( Loading waitable, UserOptions options ) ->
            waitable
                |> MultiWaitable.wait3Update2 options
                |> Tuple.mapFirst Loading

        ( Loading waitable, BookmarksFetched bookmarks ) ->
            waitable
                |> MultiWaitable.wait3Update3 bookmarks
                |> Tuple.mapFirst Loading

        ( Loading _, AllFinished user options bookmarks ) ->
            ( Loaded user options bookmarks, Cmd.none )
```

MultiWaitable module essentially provides statemachine-like functionality. Internal implementation of this package actually uses multiple `Maybe`s to keep track of status of what has been done and what still not, but it is well encupsalated.

`wait[N]Update[N]` function MultiWaitable provides returns a tuple contains updated model and Cmd which publishes a completion Msg registered by `init[N]` function that initialize MultiWaitable at first. Once all waiting state internally kept is marked as completed, `wait[N]Update[N]` function emits a Cmd for the completion Msg.

What this package brings developers is exaclty a great concentration on Model designing itself. You would be able to design your Model in a descriptive way without any contextless, distractful multiple `Maybe`s.

# Cleaner model, greater maitainability
Model is exactly the place where has the bigger portion of clue to know how your application works at a glance, **but it gets massive in no time when the application grows.** This is no doubt.

Clean model does not let you misunderstand your application and reduce bugs in  implementing new features. Designing loading state in a clean way is one of the easiest things we can tackle right now.

Always keep eyes on your Model and let it have enough context.