Here’s how I’ve come to think of it:
- 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. - 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 thanonInput SetEmail
, because “what happened” is that the user entered an email address. It’s up toupdate
what to do based on what happened. - 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 aJust Credentials
), ifupdate
will need thoseCredentials
to send the HTTP request that records the Favorite, I’ll store theCredentials
in theClickedFavorite
message that gets sent fromview
. This way, insideupdate
I don’t need to do acase
on theMaybe Credentials
and have a “this should never happen” branch. I used this technique inelm-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.