4 ms·
> The datastore is a massive global. A single global state machine and a bunch of small state machines can be losslessly transformed from one to the other. And
by dwohnitmok 6y ago
> The datastore is a massive global.
A single global state machine and a bunch of small state machines can be losslessly transformed from one to the other. And indeed Redux offers tooling to transform from one to the other depending on preference (this is the Redux Toolkit).
And indeed in that world they don't have to be bundled together. You can have separate state machines for different parts of the page.
> Robbed of the concept of benign localized state, all state transitions must be weaved through the entire complex functional tree that renders it.
As you say, this is really just a specialization of 1 right rather than a separate problem?
> They're called classes.
Classes of course can model state machines since any Turing complete style of programming can model state machines. However, they are significantly different from the classic representation of state machines in that the important part of representing state machines is that you can inspect their state. Classes are meant to be opaque; as you say their encapsulation is a feature.
But their opaqueness prevents the usual state machine composition, which is to link different state machines together based on the states they are currently in. Whether or not that's a good thing depends on the circumstances, but they make it painful when you really do want to represent things as a state machine proper, and not simply as a stateful component. And to the extent that you want a UI to be a state machine, that's a bad thing.
Of course not all parts of your UI should be a state machine, especially things that involve continuous states rather than discrete ones. For truly mind-numbingly benign stateful details (such as which frame a button should be in while performing an animated transition from one color to another) you can just put it directly in the React component and ignore the store altogether, and that's definitely where you really don't want explicit state machines, but rather encapsulated black boxes that are stateless from the perspective of the overall system (which is effectively what a component-based/class-based system looks like when interacting with a state machine).
- nsonha 6y ago> the important part of representing state machines is that you can inspect their state Why? Is that because you need to know if a transition is valid for a given state? Do you have a citation for this? > Classes are meant to be opaque; as you say their encapsulation is a feature. This is true, but in practice, people do expose things in classes. I regularly implement state machines in classes as immutable builders. > But their opaqueness prevents the usual state machine composition. Not sure what you're refering to by this. Sounds like it's based on a specific implementation. I'm sure there are patterns that could help with this.
- dwohnitmok 6y ago> 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).