Why state machines?
Based on the tutorials by @thure.
States. The final frontier. These are the voyages of an enterprising developer. Her eternal mission: to explore strange new techniques, to seek out better ways to engineer for mental models and new design patterns. To boldly go where a few awesome devs have gone before.
So you’ve found our poignant guide to SCXML and surely you’re wondering “Why should I want to go out of my way to use formal state machines?” or something like that. Hopefully this introduction addresses that kind of question.
An example: Nancy’s RPG
The problem
Nancy Drew is authoring a fantasy RPG, and some of the NPCs are vampires, for example. Vampires are complicated: if they’re alive, you can chop them to smithereens, but eventually they’ll come back; if they’re impaled by a wooden stake, that puts them in torpor; and if they’re alive or in fleshy smithereens or in torpor, and you burn them, they’ll die forever.
She wonders what the best way to represent those states of being are so that it can also scale. This has to run in the browser, so her options are limited. She likes React, but components’ state
property is static. For vampires, state
could have the values intact
, disabled
, torpor
, or dead
, though it’s the transitions between those states that are complex. Other agents will be able to change a vampire’s state, but only certain transitions between those states are valid.
Vampire components could have a changeState
method, though for this use-case it becomes a complex dispatcher with one big switch
block full of if
blocks:
function changeState (action) { switch (action) { case DMG_THRESHOLD_STAKED: if(this.state === 'intact' || this.state === 'disabled'){ this.state.set('torpor'); } break; case DMG_THRESHOLD_DISABLED: if(this.state === 'intact') { this.state.set('disabled'); } break; case HEAL_THRESHOLD_FULL: if(this.state === 'disabled' || this.state === 'torpor'){ this.state.set('intact'); } break; case DMG_THRESHOLD_BURNED: if(this.state === 'intact' || this.state === 'disabled' || this.state === 'torpor'){ this.state.set('dead'); } break; } }
This, Nancy decides, (and for the sake of this tutorial,) is ugly. It won’t scale well if different states are introduced, and in general this pattern doesn’t represent the true, complex nature of a character’s abledness. She certainly doesn’t want to deal with dispatch trees like this when she needs to represent many different types of characters and their diverse states.
Enter state machines
A character’s status in general is more accurately represented by nested categories. Are they alive or dead? If they’re alive, can they move? If not, what will rehabilitate them? What kills an angel? A werewolf? An elf?
Nancy feels state machines are the best way to represent these conditions. Since state machines interpret statecharts, and those can be represented both visually and in machine-readable SCXML she’ll have an easier time both designing and developing the different types of characters.
Instead of the ugly dispatch tree, the vampires’ conditions are represented by this SCXML document:
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" initial="alive"> <state id="alive"> <state id="intact"> <transition event="chopped-up" target="disabled"/> <transition event="staked" target="torpor"/> </state> <state id="disabled"> <transition event="healed" target="intact"/> </state> <state id="torpor"> <transition event="un-staked" target="intact"/> </state> <transition event="burned" target="dead"/> </state> <final id="dead"/> </scxml>
What is SCXML?
There are many dialects of state machines out there, and many are proprietary. SCXML is an open standard for state machines by the W3C. It describes an XML application, which specifies a syntax (a set of rules that define how you write XML code to define the state machine), and a semantics (a set of rules which describes how the state machine should be executed). The SCXML syntax is formalized by an XML schema definition, and the semantics is defined in pseudocode, and formalized by a test suite.
Nancy can use SCION to visualize the above SCXML as a diagram:
What is SCION?
SCION is a suite of software libraries for working with SCXML in JavaScript. It provides:
- runtime for executing state machines
- compiler for SCXML
- visualization
- linter
- graphical debugger
- other tools and utilities
SCION powers the examples on this site.
How to run a state machine?
Nancy can create a small environment to run the state machine, with buttons to send events as input, and pictures to illustrate the current state:
Click a button to send an event | Visualization of the running state machine |
Picture of the vampire's current state |
Click to reset the state machine | Click to show source code |
This, Nancy feels, is more elegant, scalable, and easier to read, talk about, and modify. It’s also event-driven, which gels well with the rest of the RPG which has, so far, been developed around events.
So what are state machines good for?
At this point you’re probably thinking “Alright, state machines work well for Nancy Drew’s problems, but will they work well for mine?” Good question.
Does your application have well-defined, named states? Does it receive events? Does it flow between states in response to those events? Then state machines might be a good solution for you.
‘State’ is a pretty good name for the phenomena SCXML can describe – when you’re developing software for discrete objects whose behaviors can be described in discrete groups, and those groups change depending on how the object is acted upon or what the object encounters, then you’re looking at an ideal use-case for statecharts.
Users are themselves complicated and stochastic, but they like it when their appliances and applications operate in simple, clearly defined ways. This applies to many facets of those appliances/applications: the way users interact with them, the way they provide feedback to the user, and the tasks they help the user accomplish.
Here are a set of application domains where state machines are often a good fit:
- Embedded Systems (for safety-critical control logic)
- Interaction design (as a language for communicating between designers and developers)
- Business Workflows (like flow charts)
- Customer Service (for defining phone trees, IVRs, chatbots, etc.)
There are many other domains where state machines are useful. If you have an idea, please consider contributing a tutorial to this guide.
All of that is a pretty abstract description, but you’ll find plenty of examples throughout this guide that should help illustrate how useful state machines can be.