8 ms·
Can someone recommend a good primer on _implementing state machines_? I’ve only really encountered the theory and the diagrams, but have had a harder time findi
by renlo 4y ago
Can someone recommend a good primer on _implementing state machines_? I’ve only really encountered the theory and the diagrams, but have had a harder time finding examples of actual implementations in code.
- jen729w 4y agoThe XState (JS framework) docs and community are great. Spend a bit of time hanging out there and you'll get the idea. I'm a big fan. https://xstate.js.org https://xstate.js.org
- rowanG077 4y agoIt really depends on your programming language. In C it's not much more then a switch case over an enum where every enum value is a concrete state. It languages with pattern matching and ADTs it's much more ergonomic since a state machine then is just a function from a state and an input to a new state and an output.
- Joker_vD 4y agotypedef struct State State, *StateMachine; typedef void (*Event)(StateMachine *sm); struct State { Event TurnOn; Event TurnOff; Event MakeStuck; }; void IdleTransition(StateMachine *sm); void BecomeOn(StateMachine *sm); void BecomeOff(StateMachine *sm); void BecomeStuck(StateMachine *sm); const State StateOn = { .TurnOn = IdleTransition, .TurnOff = BecomeOff, .MakeStuck = BecomeStuck }; const State StateOff = { .TurnOn = BecomeOn, .TurnOff = IdleTransition, .MakeStuck = BecomeStuck }; const State StateStuck = { .TurnOn = IdleTransition, .TurnOff = IdleTransition, .MakeStuck = IdleTransition }; void IdleTransition(StateMachine *sm) { } void BecomeOn(StateMachine *sm) { *sm = &StateOn; } void BecomeOff(StateMachine *sm) { *sm = &StateOff; } void BecomeStuck(StateMachine *sm) { *sm = &StateStuck; } StateMachine sm = &StateOff; It's still C but arguably much more concise than a switch, wouldn't you agree?
- rowanG077 4y agoFor large state machines sure. For smaller ones no. My point wasn't that enum is the only way to do it in C. Just a simple representation that is used in practice that I could explain in a single sentence.
- FpUser 4y ago>"In C it's not much more then a switch case over an enum where every enum value is a concrete state." This is but stupidly primitive representation / implementation of SM (yes it is useful in very simple cases). You can do way better in C. Somebody else already presented more advanced design.
- alain94040 4y agoThe primer is simple: if you notice that your code is using too many if statements, especially nested ones, and you are starting to wonder if you are covering all alternatives and cases, then using the rigor of a state machine may help. You know, that kind of code: if(start) { if(!running) ... } if(stopped & !jumping) { } else if(running) ... Stop and write a proper case statement (aka a state machine). You may discover that combinations of booleans (in my example, stopped & !jumping) deserve to be their own state. The insight is that the resulting code should be one clean case statement, with no nested ifs allowed. Then you know you cover all the states/cases.
- a1445c8b 4y ago“If” conditions and state machines are not mutually exclusive. You can implement a proper State machine with “If” conditions. That switch-case alternative you’re suggesting? That’s just another manifestation of the same concept behind “If” conditions. So, really, with case statements, you’re still implementing nested conditions, just in mixed ways (a mix of case statements and “If” conditions) The distinguishing characteristic between state machine and bad logic is that the top level conditions are the states, rather than the inputs. EDIT: for clarity.
- lfowles 4y agoProgramming is state machines all the way down, it's just the convenience of expression that makes a difference.
- a1445c8b 4y agoMore than just convenience of expression, it guards against "impossible states." This article[1] does a good job of explaining it. [1] https://dev.to/davidkpiano/you-don-t-need-a-library-for-state-machines-k7h https://dev.to/davidkpiano/you-don-t-need-a-library-for-stat...
- kstrauser 4y agoI had a job interview question: "In Python, write a class to implement a state machine." Me: So, each instance should represent a state, and include a list of states it could transition to, and methods to transition to them? Is that what you had in mind? Them: Well, if that's what you think a state machine is, then sure. Me: There are a million ways I could implement this. What context will this be used in? I can make sure I write something that meets those requirements. Them: Do you not know what a state machine is? Me: Of course. But how are you planning to use it? Them: I can't believe you don't know what a state machine is. So as far as I can tell, I have no idea how to implement one. (I'm so glad I didn't get that job.)
- missblit 4y agoHow to implement a state machine in Python: def next_state(state): if state == 90000: return 0 return state + 1 But more seriously; once I was asked to implement offsetof in an interview for an entry level C++ position. I didn't quite remember that this was impossible during the heat of the interview, but I did remember enough to not produce an incorrect solution that relies on undefined behavior. I didn't get that job.
- travisgriggs 4y agoI recently did an interview that went something like this too. The questions weren’t state machines. But like your experience, they boiled down to “I have some interesting experiences that feel kind of unique to me, is it possible you’ve had them too?” It was a mid level relatively seasoned person, but they admitted interviewing was kinda new. So it really came back to that “do you think we could experience a sort of cohort chemistry here” vetted by a pretty arbitrary set of “let’s find some geeky things to get connect about.” And like you, I’m not disappointed they chose to keep looking.
- userbinator 4y agoYou're overthinking it. The first reply was basically a "go ahead and do what you think is right". They're probably glad they didn't hire you either.
- 4y ago
- 01100011 4y agohttps://barrgroup.com/embedded-systems/how-to/state-machines-event-driven-systems https://barrgroup.com/embedded-systems/how-to/state-machines...
- anonymous_sorry 4y agoA key thing I've seen people miss is that actions should be triggered by specific transitions, rather than by the States. For example: If a pull request goes from Draft->In Review the FSM might perform an "assign reviewer" action. If a PR goes from Changes Requested->In Review, there is already a reviewer assigned, so it just performs "notify reviewer".
- emmelaich 4y agoI would call them two states. Or, perhaps an additional transitional state.
- anonymous_sorry 4y agoI think two states is probably a bad idea. Obviously the use case is hypothetical, but I'm assuming the set of transitions available from the In Review state does not depend on the path you took to get there.
- thebruce87m 4y agoMy states have entry() and exit() functions that do things like you describe.
- anonymous_sorry 4y agoLets say a PR can go from: Draft->Closed/Abandoned Draft->In Review Changes Requested->In Review I'm not sure "assign reviewer" would fit either in Draft.exit() or InReview.entry(). I guess you'd implement something like "ensure reviewer assigned" in InReview.entry() instead. Which would work, but would itself contain the sort of state-dependent logic that a state machine is meant to surface.
- thebruce87m 4y agoPutting it in the entry is probably what I’d do, or I’d split the “InReview” state into its own sub state machine. It’s hard to reason exactly with imaginary scenarios though.
- LtWorf 4y agoBasically an enum containing the possible states. A long series of ifs/match/dictonary/whatever to match (current state + input → next state). That's it.
- chas 4y agoIt really depends on what you want in terms of implementation. On one hand, a big chunk of digital logic is about implementing large, high-speed state machines using circuits. On the other hand, regular expressions and things like lexers are largely based on finite-state machines so there is a ton of information related to those, but they often implement things more complex than finite-state machines in order to be more expressive. In terms of manually implementing them yourself, I think the naive implementation based on the mathematical definition of a deterministic finite automata is pretty good for a small number of states for things like tracking program state. In particular, explicitly listing the total set of expected inputs/transition criteria, explicitly listing the states, and having a single function that takes the current state and current transition-relevant data and produces a new state. This representation is nice because it makes the behavior inspectable in one location. This makes it easier to notice the edge cases and prevent the state representation and possible transitions from getting spread all over code. The transition function can be a switch statement or a table. Really any way of writing a two-input function with a finite number of inputs and outputs will work. Many people have also explored strongly-typed versions of this, which are worth a look as well.
- ww520 4y agoThere're different ways to implement a state machine, table-driven, object-driven, function pointer driven, callback based, etc. If you just want a simple and hard coded sate machine, nothing beats switch statements. You can do something like the following. enum State { S1, S2, S3, S4, ... , END } State transition(State current_state, int input) { switch current_state case S1: switch input case 100: new_state = S3 do_action1() case 101: new_state = S4 do_action2() default: error() case S2: switch input case 100: new_state = S3 do_action3() case 206: new_state = S11 do_action4() case 207: new_state = S12 do_action5() case 208: new_state = S19 do_action6() default: error() case S3: switch input ... ... return new_state } run_state_machine(int[] inputs) { State state = S1 foreach input in inputs state = transition(state, input) } That's it. It's simple to understand and it's clear what the legal transitions are from each state based on the input. You can attach actions and data update on each state transition to do something useful.
- deleted 4y ago[deleted]
- munificent 4y agoI talk about a couple of implementation styles for state machines in "Game Programming Patterns" here: https://gameprogrammingpatterns.com/state.html https://gameprogrammingpatterns.com/state.html
- strangeattractr 4y agoThank you for writing this book and crafting interpreters and making them freely available. They were genuinely enlightening books.
- munificent 4y agoYou're welcome! :D
- astrange 4y agoThere are some good state chart compilers like http://www.colm.net/open-source/ragel/ http://www.colm.net/open-source/ragel/ (disclaimer - haven't used it in years) that you can look at the output of.
- presentation 4y agoJavaScript has libraries for this (xstate) but I usually just do it something like this in typescript: enum State { A, B, C }; enum Action { A, B }; type StateData = | { state: State.A, data: whatever } | { state: State.B, data: whatever } | { state: State.C, data: whatever }; type ActionData = | { type: Action.A, data: whatever } | { type: Action.B, data: whatever }; function transition(prev: StateData, action: ActionData): StateData { switch(action.type) { case Action.A: return { type: State.B, data: whatever }; case Action.B: return { type: State.C, data: whatever }; } } Then you can for example if you were using this in react do something like function MyComponent() { const [state, setState] = useState<StateData>({ state: State.A, data: whatever }); return <button onClick={() => setState(transition(state, { action: Action.A, data: whatever }))}>Go!</button>; }
- layer8 4y agoFor one possibility, look up the State design pattern. Basically, you have a variable that holds an (immutable) object representing the current state. All states that can be assigned to that variable (all abstract states the system can be in) implement a common interface (the static type of the variable), which in particular includes a method (or methods) for asking the state what the next state is given a particular event. The method then returns the new state. Whenever an event occurs, you ask the current state stored in the variable (call its respective method) to compute the next state given the current event, and then you assign that new state returned by the method to your state variable. The common interface usually comprises further methods that define/implement the behavior of the system in the given state. For example, if the states correspond to the different modes of an editor, there could be a method that implements what happens when a key is pressed in the specific state.
- userbinator 4y agoOf all the other replies, it's interesting that no one has mentioned the simplest and probably most efficient implementation, the one based on gotos: state_x: ... do some stuff ... if(some_condition) goto state_y; else if(some_other_condition) goto state_z; else goto state_n; ... state_y: ...
- mindv0rtex 4y agoAt our company, we rely a lot on https://github.com/boost-ext/sml https://github.com/boost-ext/sml
- carapace 4y agoThere really isn't that much to it. E.g. in Python you can just use a dict. # (state, event) => (next state, action) FSM = {} Add transitions (using enums): # Simple left down-[move*]-up sequences. FSM[STATE.clear, EVENT.left_down ] = STATE.set_caret, 'set_insertion_point' FSM[STATE.set_caret, EVENT.left_motion] = STATE.set_caret, 'set_insertion_point' FSM[STATE.set_caret, EVENT.left_up ] = STATE.clear, 'nothing' Then when an event happens you drive the FSM like so: new_state, action = FSM[state, event] Check out https://git.sr.ht/~sforman/Xerblin/tree/trunk/item/xerblin/gui/mousebindings.py https://git.sr.ht/~sforman/Xerblin/tree/trunk/item/xerblin/g... for an example. There is also a DOT file (and SVG image) of the resulting state graph (the code to generate the DOT file directly from the FSM dict is at the bottom of the mousebindings.py file.)
- whartung 4y agoI’ve always been fond of this pattern. state = state.next(context, data); Context just holds the global stuff of the process, data is the new input into the machine and state is the current state. A simple example is an SMTP server. State is the current command (or idle), context is email message being built, and data is the line read from the socket. Context context = new Context(); sendSMTPBanner(); State state = new IdleState(); while (!state.isFinished) { String data = readLine(); state = state.next(context, data); } Conceptually pretty simple.
- ianbooker 4y agoI am also in search for a elegant way to teach such implementations in Python. I do not want students to confuse this with OO, not sure if they see the value in functional programming so soon. My best idea so far is to teach the concept of state and state machines with Python generators…
- nemoniac 4y agoKrishnamurthi's "Swine before Perl" and "Automata via macros" papers are particularly enlightening. https://cs.brown.edu/~sk/Publications/Talks/SwineBeforePerl/ https://cs.brown.edu/~sk/Publications/Talks/SwineBeforePerl/ https://cs.brown.edu/~sk/Publications/Papers/Published/sk-automata-macros/ https://cs.brown.edu/~sk/Publications/Papers/Published/sk-au...
- 0xabe 4y agoGame Programming Patterns by Robert Nystrom has a chapter on state machines. It’s both freely available online or for purchase if you really like it and want to support him. http://gameprogrammingpatterns.com/state.html http://gameprogrammingpatterns.com/state.html