Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I have recently created some dead simple code to explain the concept to colleagues whom, as the article notes, were not using this very simple and effective construct at all to control state transitions in business apps.

As the article does not really explain how state machines work let me try to do it here so maybe you will go back to your code and put one in place :) In general, if anywhere in your code you have some property/field called status/state/etc... that you update depending on events then chances are that a state machine would improve the robustness of your code.

Ok, so you have some states. For business apps this usually comes from business. For our dummy example we will simulate a day (using Haskell here for conciseness, but no worry, it's trivial and you don't need to know any Haskell to understand it):

    data State = Awake | Dressed | Fed | Ready | Work | Cafe | Home | Asleep
Now most apps stop here and thus they don't have a machinery to control how to move between these. If we were to draw a graph where vertices are the above states and edges are how we go from one to another then, without any restriction, we would implicitly get a complete graph where you can go from any state to any state. Eg. from "Ready" I could go straight to "Asleep" without "Work" :) Our aim is thus to restrict transitions from one state to another to transitions that actually make sense. Making sense obviously depends on the problem we are solving. For our example let's introduce the following events:

    data Event = Alarm | Dress | Eat | GoToWork | Think | Yawn | Coffee | GoHome | Read | WatchTV
Now that we have states and corresponding events we can draw a graph where vertices are states and edges are events. The graph is now explicit in that we can only move between states (vertices) via events (edges). To put this graph into code, let's create a function that takes a pair of state and event, eg. (Asleep, Alarm) and returns a new state, eg. Awake:

    myDay :: (State, Event) -> State
As said above, myDay is a function that takes a (state, event) pair, which we can also call a transition, and returns a state. This function is nothing more than a lookup table, aka. transition table, where we will write down how to transition between states:

    myDay (Asleep,  Alarm)    = Awake
    myDay (Awake,   Dress)    = Dressed
    myDay (Awake,   Eat)      = Fed
    myDay (Dressed, Eat)      = Ready
    myDay (Fed,     Dress)    = Ready
    myDay (Ready,   GoToWork) = Work
    myDay (Work,    Think)    = Work
    myDay (Work,    Yawn)     = Cafe
    myDay (Cafe,    Coffee)   = Work
    myDay (Work,    GoHome)   = Home
    myDay (Home,    Read)     = Asleep
    myDay (Home,    WatchTV)  = Asleep
    myDay (_, _)              = error "invalid state transition"
That's it, we have codified our graph. We explicitly stated the valid transitions and anything else is by definition invalid. We are pretty much done at this point. A state machine itself can be thought of as a generic function that takes a transition table, a transition and returns a state. This is only to decouple a specific transition table from the general machine itself, but it literally does nothing other than lookup whether the transition is in the table. If yes then it gives back the corresponding new state otherwise we get and error.

You can see a pretty picture of the above graph and some code here: https://github.com/pwm/fsm



Brilliant. Superb explanation! Just couple of days I was trying to understand this from my team mates and it took him 30 mins to make it complicated.


Excellent explanation. Thanks!


Well done. The puml script was a nice addition!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: