6 ms·
Can anyone provide an example of a situation where something like this is a pragmatic solution? Please also provide the drop dead simple version too and explain
by runawaybottle 5y ago
Can anyone provide an example of a situation where something like this is a pragmatic solution? Please also provide the drop dead simple version too and explain why this would be more elegant and intuitive in comparison.
Types of examples I am not interested in seeing:
- a DropDown list using a state machine (or something similarly simple that’s been solved a million times in a simple way).
Edit:
I just think stuff like this is too weird and dangerous to evangelize, and I’m at my wits end in dealing with simple components that have been over abstracted.
- davidkpiano 5y agoLots of good examples here: https://xstate-catalogue.com/ https://xstate-catalogue.com/ It seems like you're just unfamiliar with state machines. They're not a "new, weird abstraction", they're a core concept of computer science in general, and it can be argued that most code we write is itself an abstraction on top of state machines; not the other way around. For more information on state machines and statecharts, I recommend reading this great resource: https://statecharts.dev/ https://statecharts.dev/
- runawaybottle 5y agoNo, I know what state machines are. What I’m contesting is the argument that this is an intuitive way to structure code. It could be intuitive to reason about, or even white board the states your application can be in, but this doesn’t look like concise and elegant code. From your examples: https://xstate-catalogue.com/machines/confirmation-dialog https://xstate-catalogue.com/machines/confirmation-dialog I’ve written a million confirm-dialogs in my lifetime, and that example is one of the most convoluted things I’ve ever seen. Guys, just because it’s in Computer Science, doesn’t automatically mean it’s a more sophisticated solution. The term for this is over-kill.
- davidkpiano 5y agoNobody's forcing you to use this library. Please take your negativity elsewhere. EDIT: Okay, didn't mean to interpret criticism as negativity; I apologize.
- runawaybottle 5y agoFair enough, but the echo chamber is why stuff like this gains prominence. I’m abrasive for sure, but I need to see some push back on these ideas in all of these threads.
- kaoD 5y agoCriticism is not negativity. It's valuable when someone in the room challenges ideas and pushes back over-abstraction (if done respectfully and constructively).
- jhgb 5y agoCan you even describe the use of a state machine for UIs as "over-abstraction"? To me it seems more like an "under-abstraction", since one usually compiles a more abstract definition into a state machine - often a regular expression or some kind of grammar.
- runawaybottle 5y agoWas just discussing this with a friend this morning. It might be better to describe it in terms of trade-offs versus the overuse of the ‘over-abstraction’ allegation. So, in a lot of cases in the UI, we are turning what is very recognizable and traditionally understood code into a new form to fit the state-machine world view in a more 1:1 way. Again, it’s a trade off. What did we gain from this transformation? I don’t think we get enough back from this trade to structure our code in this new way. Or to be clear, why would I trade my ability to read a few simple fetch calls that update a simple DOM element for this version? What is the outsized return here? Just my 2 crypto.
- gbear0 5y agoDid you start programming with OOP or Functional programming? Did learning how to programming in the other form seem difficult to understand why you would do things that way, or seem obtuse vs just using the ways you already know? I think using state machines is just a very different way of looking at code, and it does take some mind bending to think that way; but once you do, it's much easier to blend the code together the same way you'll see imperative and functional code blended together these days and not think twice about it.
- fuzzy2 5y agoI think your problem is that many things don’t have to be a state machine. And you’re right. It’s simply not a good idea to create a confirmation dialog like that. However, some things just _are_ state machines. You don’t usually see them in the GUI (except for example wizards), but many parsers can trivially be implemented with state machines.
- matsemann 5y agoI actually believe there would be fewer UI bugs if more UI was modeled as a state machine. After all, all diagrams and design is often based on "when X is shown and Y is pressed, we go to Z". But in code that's expressed in a way that makes it possible to have a mismatch between multiple half baked states.
- deleted 5y ago[deleted]
- jhgb 5y ago> It seems like you're just unfamiliar with state machines. You're saying that he doesn't know computers? Computers are state machines. Presumably he used one to post his comment.
- enjeyw 5y agoI’ve needed to construct a chatbot-esque UI on more than one occasion. I used a state-machine on the second time round and it simplified work considerably!
- cacozen 5y agoI think using a state machine can make the resulting code more elegant in some cases by coupling state changes and effects. But that’s not why they are useful. In my experience, they have been useful because they help map (even mentally) all individual possible states a piece of code can be in, and how the code should behave in each case. In the process, I usually discover and deal with more edge cases, more combination of states and behavior, and as a consequence the code is bigger/fatter. But also more robust.
- RobertKerans 5y agoThis is important, and it's probably the main reason I like using them so much. But then surely the comeback is that what I'm then attempting to do is produce a diagram in code, when I could just write the diagram on paper, check it, then write some (possibly simpler) code. Not in all cases, naturally, but often?
- runawaybottle 5y agoYeah, I think you articulated that better than a lot of us here. That, in a sense, your diagram becomes your psuedo-code, or even further, your actual implementation. My initial defensive impulse to this topic was mostly to take a stand against frontend code that is becoming increasingly obtuse over the years.
- RobertKerans 5y agoAuth logic or similar. Somewhere area in app that may be genuinely complicated and can't go wrong, where you need granular control over a set of states which often have sub states, and where there are things like remote requests to different services occurring. And you can just write this stuff as basically lots of nested if statements but... As an actual example, I have a state machine in an app I work on: 1. check if there's a session (if so goto 4). 2. If not go to email input. 3. On submit go to password input. 4. authorized. However: 2 and 3 and some of the time 1 have network requests, so handle those. Password can be entered wrong three times (error message changes accordingly, oh also no. retries are modifiable at admin level), which bumps user back to 2 (error message changes accordingly). User may have turned on PIN security, if so that will be an extra step before allowed into app. They may also have it turned on, but not one set, which is a slightly different flow. And they can turn it off/on from within app. Oh and biometric, same deal as PIN. Also can drop back to username/pword instead of OTP in some circumstances. That's most stuff I think off top of head. That's a hierarchical state machine written using XState. It's easy to test (UI is seperate from the machine, and I can just plug in API functions for the remote calls). It's relatively easy to read (xstate API produces stuff that reads like a diagram, it's easy to explain what's going on). It's easy to add/remove states
- runawaybottle 5y agoThat’s actually a good use case, thanks for sharing.
- RobertKerans 5y agoJust to note, I think I do agree with you. With the above flow, it's behind an interface that just provides update functions, the current top-level state, and the values that are updated by it (the user object the remote calls return from my Auth provider, for example). All the machinery is completely hidden: I had previous versions where it was more exposed, but it asks a lot of every other dev I work with to learn yet another complex API. There are other potential uses for in my apps (ex. backing a numeric input made up of multiple inputs for PIN/OTP that needs to track focus state + a few other things), but I've been slightly wary about introducing more xstate code. I feel like: - explicit state machines are extremely useful. - they're very well understood by embedded/game devs, but not so much by web GUI devs... - ...but the latter have an innate understanding due to much of the things they build often being implicit state machines. And those things are often fine as they are. - adding an explicit machine greatly increases granular control at the expense of [complex] boilerplate. - the simple fact that they are so explicit (and that you get one state at a time) means state machines aren't some silver bullet for UI, and they get complicated really fast due to state explosion. - I think any game dev would be able to explain why you don't just use them for everything, and I find it slightly strange that there's little input from game/embedded devs when these things come up on HN -- maybe I'm just assuming there's some overlap of knowledge when actually there isn't. - heirarchical state machines and state charts fix the state explosion issue, but then bring with them a more complex API that needs to be learnt. I'd say, for web/JS, XState is gold standard and it's a fantastic library. Explicit state machines are very useful, but writing ad-hoc machines that do anything useful isn't particularly pleasant in JS; XState, with its serialisable object configuration, is pretty easy to explain. It has the advantage of being completely agnostic w/r/t front-end library, which I think is critical. I can shove it behind an interface, and if necessary replace it with something else pretty easily. I'm somewhat uneasy about having it directly tied to [in the OP's case] React, because I'm in agreement that it's baking clever, complex code right in (yes, for anyone disagreeing, I am aware state machines are a simplification. So is Redux. So are observables. Hell, so is recursion. Anyway).
- gbear0 5y agoI think the value of a state machine is treating all of the associated states in a single conceptual model or level. You can definitely do everything directly in a bunch of if statements and tracking state in separate variables, but that opens yourself to more potential bugs, higher maintenance cost, less big picture understanding, and even larger performance costs (separate codes means the compiler can't optimize as well; means hardware caches aren't as effective; there's more overhead to run separated functions; etc, etc). Here's hopefully a more apt example: imagine wanting to have a multiple document interface (MDI) that shows a bunch of 'window' like views. Some examples of the idea are react-grid-layout, golden-layout, react-mosaic. Each window can be minimized, maximized, moved, flashed, and closed ... all with flashy animations. You could create a whole bunch of components that allow you to capture all the different states and toggle them in different components, and effectively have the MDI business logic sprinkled all through out YOUR code, not just the 'library' code. Alternatively, you could use a state machine that captures all the states in a single spot and manages that all efficiently and safely, you just call an api to trigger state changes throughout your code. As some other comments have mentioned, this is a fundamental part of computer science. You can get by without knowing it cause you can write a bunch of ifs and state logic all over the place. If you only know how to use a hammer, ya you could still hammer a screw in. Understanding how different data structures of different patterns fit together provides you new tools to do things in different ways when appropriate. Learn how to manage complexity, not fear it, cause there's nothing dangerous about state machines. In fact, I'd argue that anyone that thinks state machines make things more complex just isn't looking at a large enough scale because state machines should make things less complex by wrapping all the complexity inside them (when used appropriately).
- runawaybottle 5y agoI’ll concede I came with a contentious tone, which is probably resulting in a similar retort by a few of you that is mostly using appeal to authority to justify your claims (eg just learn computer science bro, you must not know it). Sporadic business logic/spaghetti code is a problem in any application. State machines will not magically avoid this. In fact, it should be just as susceptible to it. When sphagetti code shows up under a complex architecture like this, you could be in a world of hurt. There won’t be a few if-statements for you to unwrap, but instead a maze of cascading state updates. Another common thing I’ve seen is the granularity of capturing any and all state updates, and then some. It’s very tedious. Anyway, I’ve been dead wrong about 70% of things in life before, so I’d be happy to look at a non trivial app written with xstate if anyone’s got a repo.
- brucou 5y ago:-) I feel you. I like to think that state machines are used where they are useful (in some games, process orchestration, generators, embedded systems, etc.). The question is more where else can they be useful given that the low hanging fruits have already been harvested. I am using state machines to write reactive applications: https://brucou.github.io/documentation/ https://brucou.github.io/documentation/ So far I have to say that it is a mixed experience. There is a cost to the abstraction and the indirection (that is fairly well known easy to describe even if we rarely do so due to some self-imposed no-negativity bias or having some interest in the game), and then there are the benefits that are less easy to describe because they depend highly on the nature of the problem that you are addressing. I implemented a Medium clone application (https://codebase.show/projects/realworld https://codebase.show/projects/realworld) with state machines. The result is 47Kb (brought down to 39Kb after compiling the machine and other optimizations) vs. 70Kb for the Vue implementation or 160 KB for the React/Redux one (that implementation piles abstraction over abstraction in the form of libraries and pays the corresponding price). I would count that as benefit. Also cf. https://brucou.github.io/documentation/v1/tutorials/index.html https://brucou.github.io/documentation/v1/tutorials/index.ht... for the full pitch. The high-level machine is that one: https://brucou.github.io/documentation/graphs/real-world/realworld-routing-article.png https://brucou.github.io/documentation/graphs/real-world/rea... At this level, you can follow the routing of the app. Every route is a compound state. If you open it, you see the details of the behavior. The full machine (post refactoring) is like this: https://brucou.github.io/documentation/graphs/real-world/realworld-with-subscription-syntax-experiment.png https://brucou.github.io/documentation/graphs/real-world/rea... So one advantage is that with those graphs, it is easier to onboard new folks arriving to the codebase. They just have to follow arrows to know what the code is doing in response to a series of inputs. I could continue but with all that said, the Hyperapp implementation of the same application is 27Kb and also fairly simple (even if arguably not as simple) to get into. So there isn't a clear-cut ex-nihilo benefits to state machines here. In fact if you would have implemented the same application as a MPA instead of a SPA, for most, if not all of the pages, using a state machine to model the behavior is simply overkill. Most of the job and value of the machine in my example is to do the client-side routing done in the machine (so no extra library cost). Anyways the bottom line is the tool is useful but you have to figure for what, and where is the value maximized. The obvious cases have already been figured out.