I think, because of the mention about controlling mutability/side-effects, "object" in that post means OOP-style objects which mutable state. It's not an issue with immutable objects/structs (or I guess "records" which is the term I'm used to with functional languages).
functional languages still have to deal with storing and manipulating changing value and state, and it is still often advantageous to hide the complexities of that storage and manipulation behind simple interfaces throughout the codebase, using opaque values with helper functions to manipulate them.
OOP as a pattern, rather than a language construct.
the great advantage here is that all manipulations are local. there is no spooky action at a distance in an immutable language.
I just use simple tail-recursion in an event loop to help manage state.
let rec eventLoop state =
let input = getInput ()
let newState = update input state
drawState state;
eventLoop newState
The update function takes an existing state and an input record (key events, etc.), returning a new state (which may have nested records within itself but it is all immutable).
New state is kept by the recursion (passing new state to the same function). The only IO is getting input and drawing, which are inherently side-effecting operations.
I do have nested records, but (at each stage) they get joined into a new parent record. No mutability there.