OK, to think of it some more, mandating mutation does seem reasonable if we accept the premise:
- An object returned from a functional interface can be passed to a mutating procedure.
Then, a functional object needs at least one indirection (handle -> actual structure, where functional updates always allocates a fresh handle), if it wants to avoid copying everything every time.
Then, mandating mutation is no problem even if the underlying structure is immutable, since the mutator can create a new (updated) structure and change the pointer of the header.
Yeah, that's consistent and simple to reason about.
I was worried about the overhead of extra indirection, which wouldn't be necessary either if every interface is functional or if every interface is side-effecting. But yes, if we want to provide good parts of both worlds, the indirection seems inevitable.