Most of my Elm apps have some kind of ordered collection of sub-models (for lack of a better term), and a lot of Msg types pertaining to updating each sub-model. I think this is pretty typical, but the ways in which I implement it don’t feel very nice:
type alias Model = { items : List Item }
type alias Item = { uid : Int, value : String }
type Msg =
ItemMsg Int ItemMsg
type ItemMsg = ValueChanged String
update msg model =
case msg of
ItemMsg uid subMsg ->
case subMsg of
ValueChanged value ->
let
(updatedItems, cmds) =
model.items
|> List.map
(\item ->
if item.uid == uid then
({ item | value = value}, Cmd.none)
else
(item, Cmd.none)
|> List.unzip
in
({ model | items = updatedItems}
, cms |> Cmd.batch |> Cmd.map (ItemMsg uid)
)
Basically what I hate about this is List completely sucks as a keyed collection, and I’m sure that my method of updating a single element with a Cmd has to be totally inefficient. However, I feel trapped into using it since I need to preserve order, and map the whole thing to a List every time it changes to generate the HTML anyway.
Here are the downsides of other Elm collections:
Array: Slow to convert to a list. Need unchangeable UID’s, not indexes. Indexes can result in race conditions.
Dict: Lets me use UID’s, but doesn’t preserve ordering. Will always be sorted by UID, so rendering requires converting the values to a List and sorting.