Skip to content
Jens Ayton edited this page Apr 25, 2020 · 3 revisions

The Update struct contains one method: update(Model, Event) -> Next<Model, Effect>. (When specifying a loop, you can pass a closure with the same signature instead of an `Update struct.) In this section we will refer to this method as the Update function. It is meant to be a function in the functional programming sense of the word, in other words it should be pure. In order for it to be a pure function, there are some constraints you should keep in mind:

Keeping the Update function pure

  • The Update function must not have side-effects. You are not allowed mutate any shared memory, write to disk, print a log message, make a network requests, etc. Whether your function has been called once or a million times should have no effect on the rest of the application. If there is any impact you want the function to have on the outside world, you should use an Effect to describe it.

  • The Update function must not depend on external state. This is sort of the inverse of the previous statement. The only things that should have any effect on the return value of the Update function are the Model and the Event that are passed as arguments. This means no reading from wall clocks, using random number generators, reading from disk, or even reading from mutable shared memory. The only data you may use in the Update function is constants, the current Model object, and the current Event object.

  • Configuration of the Update function must be in the Model. As a follow up to the previous statement, any configuration needed for the Update function must be put in the Model object. You should not have member fields that change the behaviour of the Update function, even if they are immutable.

The return value of the Update function

There are four possible return values of the Update function: no change, only Effects, new Model, or both a new Model and Effects. These are described using the Next struct, which has correspondingly named factory methods:

Next.noChange()
Next.next(model)
Next.next(model, effects: effects)
Next.dispatchEffects(...)

If there are no Effects, then no Effect values will be emitted by the Update, and likewise, if there is no new Model, then no Model will be emitted. Take special note of that last statement - if you don’t return a new Model then no Model will be emitted. It does not matter if the Model has actually changed or not, it is up to the Update function to decide if a Model should be emitted, and therefore whether observers (like the UI) will see a new Model or not.

Tips for writing the Update function

  • Start by defining expected behaviours of the Update function. We recommend a specific modelling flow (see the Mobius Workflow), and that you start by defining the Update function using unit tests. Doing so will help you uncover specification mistakes and corner cases before you spend any time on building out the Update function and, more importantly, the Effect Handlers and the UI.

  • It is allowed to use mutable data structures inside the Update function. The Update function only has to be pure from the outside. If you can simplify the implementation by setting up temporary mutable objects, go for it!

  • It is not necessarily a problem that the Update function is big. As long as you have an easy to understand control flow, an Update function can become quite large without becoming hard to reason about. Do not hesitate to break it down into several smaller static Update functions, e.g. break out the handling of a particular Event or everything that happens before data is loaded.

  • Avoid nesting too deeply. Sometimes the Update function can become quite deep when you’re for example first switching on which Event just happened, then which state you are in, and finally take action depending on data in the Model. In these cases you usually break out smaller functions that handle just this particular state, Event, etc. You can also use other regular techniques such as early returns, using local variables to store intermediate values, or using tuples to switch over multiple values (such as event and model state) at once.