Message types carrying new state

Here’s how I’ve come to think of it:

  1. A message’s job is to carry data to update. I think it’s generally a mistake to put a function in a message, both from a general design perspective as well as because it means the message won’t work in the debugger anymore.
  2. A good guideline I’ve found for message design is to have each message answer the question “what happened?” rather than “what should update do?” I’ve started naming messages accordingly, e.g. onInput EnteredEmail rather than onInput SetEmail, because “what happened” is that the user entered an email address. It’s up to update what to do based on what happened.
  3. Sometimes I’ve found it useful for the message to hold data not just about “what happened?” but also “under what circumstances?” For example, let’s say there’s a Maybe Credentials in the model because the user may or may not be logged in. If I’m rendering a Favorite button that only gets rendered when the user is logged in (meaning the model holds a Just Credentials), if update will need those Credentials to send the HTTP request that records the Favorite, I’ll store the Credentials in the ClickedFavorite message that gets sent from view. This way, inside update I don’t need to do a case on the Maybe Credentials and have a “this should never happen” branch. I used this technique in elm-spa-example.

That said, there are potential pitfalls to be aware of when doing (3). Storing information in a message that will be returned by a Cmd, for example, can definitely cause problems if the information becomes stale between when the Cmd is fired and when it ultimately delivers its message—potentially several seconds later.

I’m guessing the autocomplete bug you encountered was on a release of Elm prior to 0.19, because onInput now synchronously triggers a re-render each time it fires (precisely because of synchronization issues like this), so I don’t think the bug you encountered would still be reproducible on 0.19. I could be wrong though!

Still, even if it did not cause a race condition, I wouldn’t choose to store the whole Item in the message, because that would mean view would be eagerly updating each Item just to set up the handler. That’s unlikely to cause a performance problem in practice, but it does mean view is doing a lot more than setting up a “here’s what happened” message. I would rather have all the logic for “what to do based on what happened” live in update, even if it means update has to handle the “what that ID is not in the collection” case that I expect never to happen.

26 Likes