A mutable object keeps one identity while its internal state changes. Calling an operation such as UpdateState can alter encapsulated data rather than return a replacement value, so later code observes a different state from the same object. section 4.2.2
This model is familiar and sometimes useful, but it makes behavior depend on the sequence of earlier calls. Shared mutable state can leave an application in a hard-to-reproduce state, and concurrent updates can introduce race conditions even when local testing appears successful. section 4.2.1
Figure 4.3 Modifying mutable state on an object by calling a method. Every call to UpdateState() modifies that single object over time.
Follow the single object across repeated UpdateState calls: each operation changes its state in place rather than producing a separate state for the caller. section 4.2.2
An immutable alternative makes each operation return updated data, which is passed to the next operation. This makes state transitions explicit. Copying is not automatically a performance problem: outside unusually update-heavy loops, costs may be insignificant beside database access or JSON deserialization. section 4.2.2
A car gas tank
In F#, a mutable gas value starts at 100.0, and drive changes it with <-: a far trip halves it, a medium trip subtracts 10, and a near trip subtracts 1. The immutable version instead accepts gas and distance, returns a new amount, and names each resulting state before the next drive. section 4.2.3
F# permits mutation but makes it explicit: mutable declarations need the mutable keyword, and <- performs assignment, while = tests equality. Prefer immutable data and expressions where practical; doing so can reduce the cases that tests must cover. Keep narrowly scoped mutable helpers acceptable when they efficiently support a pure result. section 4.2.3 section 14.1.1 section 15.2.3



