5 ms·
I have exactly one negative thing to say about state machines: they are difficult to understand when all you have is the implementation, which makes them diffic
by probably_wrong 3y ago
I have exactly one negative thing to say about state machines: they are difficult to understand when all you have is the implementation, which makes them difficult to update.
My solution is typically to include an ASCII drawing of the state diagram in the comments, which I never really liked as a solution for reasons I can't explain. But I also think that this is a fair price to pay when the alternative is a jungle of "if condition1 and not condition2...".
- stinos 3y agothey are difficult to understand when all you have is the implementation, which makes them difficult to update I don't really have that issue, could you give an example? I typically write them with a helper like (not web app, but shouldn't matter) fsm.In(State.Idle).When(Command.SomeStuffHappened).GoTo(State.Foo).WithTransition(InitiateRun); fsm.In(State.Idle).When(Command.OtherStuffHappened).GoTo(State.Bar).WithTransition(InitiateRun); ... fsm.In(State.Foo).When(Command.FooAborted).GoTo(State.Idle) ... which for me is a lot easier to reason about than reasoning about state generally is (i.e, very hard especially if you have to look at the combination of a couple of state variables and then also have to take preconditions into account, god forbid other threads, well, you know the drill probably:)
- giraffe_lady 3y agoI think there is kind of a figure/ground effect where there's two ways of looking at a given state machine but it's impossible to hold both in your mind at the same time. You're either thinking about the "path through" and the effects along the way, or you're looking at a single state and its valid transitions. Both are both, obviously, but to make a change to an existing machine you need to start somewhere. The helper fns, or named types, or whatever approach, will have to commit to one of those "views" leaving you on your own for the other one. Your example clearly shows the "paths through" eg the start, end, and effects. But to modify it you need to focus on a single state, and figure out which other states and transitions are available from there, which it doesn't help with. You could phrase the helpers instead to make the transition map clearer, but then you obscure the start/end/effects view. I'm maybe not explaining this well but it's a problem I've run into in some way with every state machine after its implementation. They are like regex they make sense when you're building it and have all the context but later it's hard to put it back together.
- stinos 3y agofigure out which other states and transitions are available from there I think I see what you mean (for me this is just another manifestation of 'reasoning about state is hard - like possibly the hardest thing in programming), and in a case like the example shown you start by looking that up by looking at the definition; it's bascially also why I left the newline between the Idle and Foo states: want to know what states are available from Idle? Look at all lines starting with In(State.Idle). Now, I assume you also figured that out so perhaps the state machines I've used have never been big enough (largest would be like 100 of these lines) to really see the problem you're facing. Or maybe you've been often looking at machines where states aren't very well defined meaning a state isn't 'small' enough making it really hard to figure out where to go from there (state machine inside another one could potentially be a solution there)? Or perhaps a state machine wasn't the right solution after all?
- mistrial9 3y agoMoore Machine or Mealy Machine ? https://en.wikipedia.org/wiki/State_diagram https://en.wikipedia.org/wiki/State_diagram
- jsmith45 3y agoThat's not bad. One limitation is that is probably a builder pattern, creating an object that runs an arbitrary state machine. (I suppose it could be a method repeated rerun, where fsm is a wrapper around the current state, and method calls that don't apply return a null object singleton, but that seems less likely). If so it will have a few downsides, like extra memory needed to build up the transition table(s), and possibly being less efficient in executing than a hard coded implementation, simply because the FSM type would need to implement code for arbitrary FSMs, meaning potentially less efficient than scenario specific implementation. In many scenarios this additional overhead is probably not a big concern, and the increased maintainability vs some other styles of coding FSMs may be well worth it. I'd guess the built up data structure data structure would be something like a dictionary that maps from the current state to a dictionary that maps from trigger conditions (or commands in the code above), to a struct that contains the new state, and optionally a transition action function pointer (or delegate, or whatever your language calls them). One of the interesting things about FSMs is that there are many ways to code them. I've also seen an thin wrapper around state object with methods for all transition actions, said methods being mostly a switch statement (or if/else chain) around the current state, setting the new one and possibly calling an transition action. Plus of course there are plenty of ways to code an FSM such that its existence is more implicit than explicit, where the main hint that an FSM exists at all the existence of a variable named state.
- stinos 3y agoI'd guess the built up data structure data structure would be something like a dictionary that maps from the current state to a dictionary that maps from trigger conditions (or commands in the code above), to a struct that contains the new state, and optionally a transition action function pointer (or delegate, or whatever your language calls them). That's about right yes. This particular thing was in C# in a fairly large desktop application, and the little extra memory needed for this or potential performance overhead of a dict wasn't a concern at all in the greater scheme of things.
- skywal_l 3y agoThe problem is that the code and the diagram will eventually diverge, especially if multiple people are working on the same code base (which is why you have a diagram in the first place). There used to be software like Rational Rose were you would draw your state machine and then have the code generated but the code generated is hard to maintain and debug and ends up drifting away anyway.
- tourgen 3y ago[dead]
- criddell 3y agoI use a plugin for Visual Studio that lets me add an image to comments and it’s wonderful even if it is a bit janky. If you don’t have the plugin, you still see a reference to an image file that you can click on to load in an external viewer and this a decent fallback. I don’t know why this hasn’t become a standard feature of IDEs and editors.
- JohnFen 3y agoI would hate this so much! I can see how an actual image is useful in certain circumstances, but I wouldn't want it to appear in the code itself. Just include the image in the documentation folder and refer to it in the code by name.
- criddell 3y agoIt's been invaluable to me in a few circumstances. A common one is when there is a diagram of a physical object with dimensions and the dimensions are used in the code. For example, say you are doing a volume calculation of a complex enclosed space that uses quantities like length, height, clearance, diameter, etc... A diagram illustrating what those dimensions are can aid immensely in understanding the code.
- JohnFen 3y agoOh, sure, it can be extremely helpful. A picture is worth a thousand words and all of that. I just don't want that picture in-line with the code. It bulks up the view of the code and I'd want the picture in a separate window anyway, to refer to it as I'm scrolling through the code itself.
- Xenoamorphous 3y agoI don’t think I’d ever 100% trust a diagram (of a state machine or anything else, really). They become out of sync way too easily.
- zero_shift 3y agoYou can visualise many FSMs automatically - see this project of mine for an example: https://github.com/jbreckmckye/robot3-viz https://github.com/jbreckmckye/robot3-viz
- govolckurself 3y agographviz on a dot file to PNG, and then slap that bad boy in the README. Boom. Done.