The “I’m Stupid” elm language nugget #6

End part of a series on component hosting in elm. Part 1, Part 2. Repo here. I’ve been looking back through the notes I made and replaying the isolated diff of just the snippet I’m looking at into this repo as I write. Let’s get this over with. I will start writing about shorter topics at a more regular interval.

On with the commits:

Here I was making things more uniform with the elm convention of consuming arguments in action model order so models can be mapped over, or multiple effects on a model can be chained. This is just some cleaning, and not really related.

And we’re at one of the points we’ve been waiting for; the ability to map over component updaters!

At this point, we’ve got a list of submodels in .displays in our main model, and this list of component updaters, the hearts of which are the set of just the unfactorable code (hopefully) that either wraps or unwraps each submodel instance and unwraps or wraps its actions and effects.

It’s worth pointing out that in the end, this takes the form of:

let puUpdate = 
msgMapper PU.update Purpose PurposeModel in case (action,y) of
(Purpose a, PurposeModel p) -> puUpdate a p
(_,mm) -> (mm, Effects.none)

So what I wound up deriving using a TDD like progression of tiny steps was a very compact function that uses a pattern match on a pair to match up a submodel action with a submodel and apply it. I previously wrote here about a simple shortcut to the rather primitive (model, Effects action) pairs that startapp uses, and that comes into play here. The consumer of this update function:

componentMapM action updater effmodel =
 let y = EF.get effmodel in
 let (m, e) = updater action y in
   |> (\_ -> m)
   |> eff e

Just takes the prepopulated model from the update and then batches the effects into the effect stream. If desired, multiple downstream components could respond to a broadcast message together, and all the effects would just work. Picking the submodel out and putting it back are done in componentUpdate via Container methods getDisplays and updateDisplays (is my OO showing? :-P )

So the rest is a bit of cleaning, but the code gets a little better:

Here, I remove type specific code from componentUpdate and give a corresponding method to Container.

Here, we’ve done everything we need to make this code fully generic and we’re ready to bottle up this component updating code and move it to somewhere out of the way. It’s not part of the SPA proper anymore but a dependency it relies on.

At the same time, start removing an old (no longer used) update wrapper that was originally imagined to do part of what msgMapper does.

Here, the redundant updater function is further removed.

Here’s something somewhat important mixed in with other cleaning: we recognize that the updater function for the component ( like import Foo exposing (create, update, view) ) is type specific and belongs not as an argument to the updater but in the type specific updater itself. This allows the type signatures everywhere to be fully erased.

Finally, noticing that there is still redundant code in the type specific update functions, we make msgMapper.

This code isn’t perfect by any stretch but it achieves the goal we want; we can easily incorporate lists of distinct components in our code and let them update on their own without having to worry. This has been long, but I hope I’ve added something by going through this process in public and showing (at least my way) of doing OO like abstraction in elm.