Search code examples
recordelm

How to add a nested element in a model in Elm


Is there a standard way to do the following?

{ model | country =
  { model.country | state =
    { model.country.state | city =
      { model.country.state.city | people =
        model.country.state.city.people ++ [ newPerson ]
      }
    }
  }
}

Of course, country, state, and city are records nested in the model. I just want to add a person in the nested city record.

The above doesn't actually work. I get the following error on the first mention of model.country:

I am looking for one of the following things:

"'"
"|"
an equals sign '='
more letters in this name
whitespace

The way I was able to get this to work was to simply call a function at each step:

{ model | country = updateCountry newPerson model.country }

updateCountry person country =
  { country | state = updateState newPerson country.state }

And then the same for updateState and updateCity...


Solution

  • As of today(0.18.0), arbitrary expressions are not allowed in record update syntax.

    In other words, you can not access field of a record during the update:

    model = { model.topProperty | childProperty = "Hello" }
              ^^^^^^^^^^^^^^^^^
                Syntax error
    

    It is a planned feature for Elm Compiler, but for now, you should consider restructuring your model or using one of the verbose workarounds.

    Personally, I prefer let..in expression for that, but I never use records with depth, higher than 3.

    let..in example

    It looks super-verbose, but there is nothing bad with this approach. You will be abe to refactor it when Elm Compiler will support a better syntax.

    Use this as a starting point for developing a set of helper functions for updates of different levels.

    let
        -- Deconstruct on every level.
        { model } = init
        { country } = model
        { state } = country
        { city } = state
        { people } = city
    in
        { init
            | model =
                { model
                    | country =
                        { country
                            | state =
                                { state
                                    | city =
                                        { city
                                            | people = "John" :: people
                                        }
                                }
                        }
                }
        }