> I'm sure there are patterns that could help with this.

Indeed there are. Again any Turing complete language can do anything another language can with a sufficient dose of design patterns (in the limit design patterns just recreate another language). So everything I say about "classes" and "state machines" needs to be taken as talking about level of effort not possible vs. impossible, since the former, being Turing complete in most cases, can always model the latter.

But to return to the meat of your questions:

> Why? Is that because you need to know if a transition is valid for a given state?... Not sure what you're refering to by this.

In a manner of speaking yes this is about validity of transitions, but it only shows up when you're making a bigger state machine from smaller ones.

What I meant by "state machine composition" and "important part of representing state machines is that you can inspect their state" relates to the combinatorial explosion of states that occurs when you build a larger state machine from smaller state machines.

When you combine state machines naively you get a new state machine that is the combination of all its child states (the n-tuple of all the child states). These usually (but not always) are not all valid states at the parent state level. That is usually, at the parent state level, only some subset of the combination of every possible child state is a valid overall state.

This means, if you're thinking of things purely as state machines all the way down, you don't want your child state machines to be black boxes. Instead, you want to be able to form relationships between the different child states to be able to constrain them in different ways. In a statically typed language with algebraic data types this might show up as various different sum types that are subsets of tuples. In a language without sum types this often ends up being basically the visitor pattern. In a dynamically typed language this might show up as various assertions about the overall n-tuple. Either way, you need some way to "open up" the child states to inspection by the parent state.

Now sometimes, your parent state truly is the n-tuple of all its child states, i.e. the child state can be any state at all and this is still a valid parent state. At the UI level, this is usually true for anything a user would be unlikely to care about if the page were to refresh and that state was reset, for example the state that governs the animation of a flashing button, or a drop-down menu being open or closed. This is what I meant when I said "black boxes that are stateless from the perspective of the overall system." From the parent system's perspective, if the child component is a black box, you could substitute that child component with a completely stateless placeholder (e.g. a static button) and the overall system would not be in an incorrect state.

And it is precisely these places where it is most convenient to use opaque child states to model things.

However, most "significant" things in a UI are things where we do care about constraining the n-tuple of child states to a subset. We often want certain parts of the UI to gate other parts of a UI (e.g. greying out certain parts of a UI), or want certain actions in one part of the UI to cascade in a specific way across the rest of the UI.

Don't get me wrong, getting back to the beginning of my reply, I'm by no means saying that this is impossible with opaque classes. After all there have been people making perfectly functional UIs with opaque classes for a long long time. It's merely more annoying and bug-prone.

If all your child states are opaque, then all this subsetting of valid states is implicit in control flow rather than explicit in data definitions. And as Fred Brooks put it: "Show me your flowchart [control flow] and conceal your tables [data definitions], and I shall continue to be mystified. Show me your tables, and I won't usually need your flowchart; it'll be obvious."

I'm not sure what you would want from a citation? Somebody other than me saying that the important part of representing state machines is being able to inspect their state? Would you accept prior art? Languages that are explicitly built to model state machines, such as TLA+, generally expose the state explicitly by default and build all their reasoning facilities around manipulating the state directly (TLA+ is indeed generally not an executable language, but there aren't many executable languages I can think of at the moment that really go all-in on making everything a state machine like TLA+ does and so aren't great illustrations of what pure state machines are like; Elm does this as well, but Elm and Redux are the same core approach so it doesn't really count in a discussion about Redux).