Feature Comparison
A quick look at what machines offer what functionality, across the 16 most
popular FSMs on NPM at the time of writing. Updates and extensions are
encouraged.
Definitions and a change link follow the tables.
Language features |
|
jssm |
XState |
javascript-state-machine |
machina |
finity |
fsm-iterator |
fsm-as-promised |
stately.js |
state-machine |
node-state |
fsm-event |
fsm |
stent |
robot3 |
mood |
grammar-graph |
States |
16 |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
Transitions |
14 |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
✅ |
✅ |
❌ |
Actions |
11 |
✅ |
✅ |
✅ |
❌ |
✅ |
❌ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
❌ |
❌ |
Data |
6 |
✅ |
✅ |
✅ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
✅ |
❌ |
❌ |
TypeScript data |
2 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
General hooks |
7 |
✅ |
✅ |
❌ |
❌ |
✅ |
❌ |
✅ |
❌ |
✅ |
❌ |
❌ |
❌ |
✅ |
✅ |
❌ |
❌ |
Specific hooks |
10 |
✅ |
❌ |
✅ |
✅ |
✅ |
❌ |
✅ |
✅ |
✅ |
✅ |
✅ |
❌ |
❌ |
✅ |
❌ |
❌ |
Post-hooks |
4 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Hook rejection |
4 |
✅ |
✅ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
Transactions |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Extending machines |
3 |
❌ |
✅ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
Machine composition |
3 |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
✅ |
❌ |
❌ |
Dynamic graphs |
2 |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
Properties |
3 |
✅ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Methods |
2 |
✅ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Weighted edges |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Heirarchical states |
3 |
❌ |
✅ |
❌ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
State groups |
2 |
⌚ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
Timeouts |
4 |
⌚ |
✅ |
❌ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Immediates |
1 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
Error hooks |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Input/output tape |
1 |
⌚ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
Tape validator |
1 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
Termination |
6 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
✅ |
❌ |
✅ |
Async transitions |
4 |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
✅ |
❌ |
Event emitter |
3 |
⌚ |
✅ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Random walks |
2 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
Serialization |
2 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Factories |
4 |
✅ |
❌ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
Named instances |
2 |
✅ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Automatic API |
1 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Count |
19 |
13 |
10 |
11 |
10 |
2 |
8 |
6 |
7 |
5 |
4 |
5 |
4 |
12 |
4 |
6 |
Notations |
|
jssm |
XState |
javascript-state-machine |
machina |
finity |
fsm-iterator |
fsm-as-promised |
stately.js |
state-machine |
node-state |
fsm-event |
fsm |
stent |
robot3 |
mood |
grammar-graph |
String DSL |
3 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Wildcards |
1 |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Stripes |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Cycles |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Kinds |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
State spread |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Complex labels |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Count |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
0 |
0 |
0 |
0 |
0 |
API |
|
jssm |
XState |
javascript-state-machine |
machina |
finity |
fsm-iterator |
fsm-as-promised |
stately.js |
state-machine |
node-state |
fsm-event |
fsm |
stent |
robot3 |
mood |
grammar-graph |
In-place extrapolation |
1 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
Graph reflection API |
7 |
✅ |
✅ |
✅ |
❌ |
✅ |
❌ |
✅ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
History |
4 |
✅ |
✅ |
✅ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
State histograms |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Count |
3 |
2 |
2 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
Docs, Support, and Community |
|
jssm |
XState |
javascript-state-machine |
machina |
finity |
fsm-iterator |
fsm-as-promised |
stately.js |
state-machine |
node-state |
fsm-event |
fsm |
stent |
robot3 |
mood |
grammar-graph |
Defined lifecycle |
4 |
✅ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Detailed errors |
2 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Extend existing objects |
2 |
❌ |
❌ |
✅ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Defined start states |
6 |
✅ |
❌ |
✅ |
✅ |
✅ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
Probabilistic starts |
2 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
In-source debugger |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Browser debugger |
2 |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
Compiler |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Cross-compiler |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Graph renderer |
4 |
✅ |
✅ |
✅ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Visual styling |
2 |
✅ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Manual |
5 |
✅ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
API samples |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Demo videos |
2 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Tutorial videos |
2 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Chat community |
3 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
✅ |
❌ |
❌ |
❌ |
Example library |
2 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Count |
13 |
8 |
6 |
2 |
1 |
1 |
2 |
0 |
2 |
0 |
0 |
0 |
2 |
2 |
0 |
1 |
Testing |
|
jssm |
XState |
javascript-state-machine |
machina |
finity |
fsm-iterator |
fsm-as-promised |
stately.js |
state-machine |
node-state |
fsm-event |
fsm |
stent |
robot3 |
mood |
grammar-graph |
100% test coverage |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Fuzz testing |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Mutation testing |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
i18n testing |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Count |
3 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Tools |
|
jssm |
XState |
javascript-state-machine |
machina |
finity |
fsm-iterator |
fsm-as-promised |
stately.js |
state-machine |
node-state |
fsm-event |
fsm |
stent |
robot3 |
mood |
grammar-graph |
Live editor |
2 |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
CLI |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
VS Code Extension |
1 |
❌ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Github Action |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
URL live-paste |
1 |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Linter |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Minifier |
0 |
⌚ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
Count |
3 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Totals |
|
jssm |
XState |
javascript-state-machine |
machina |
finity |
fsm-iterator |
fsm-as-promised |
stately.js |
state-machine |
node-state |
fsm-event |
fsm |
stent |
robot3 |
mood |
grammar-graph |
Language features |
19 |
14 |
10 |
11 |
10 |
2 |
8 |
6 |
7 |
5 |
4 |
5 |
4 |
12 |
4 |
6 |
Notations |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
1 |
2 |
0 |
0 |
0 |
0 |
0 |
API |
3 |
2 |
2 |
0 |
1 |
1 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
0 |
0 |
2 |
Docs/support |
13 |
8 |
6 |
2 |
1 |
1 |
2 |
0 |
2 |
0 |
0 |
0 |
2 |
2 |
0 |
1 |
Testing |
3 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Tools |
3 |
2 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
0 |
Sum totals |
43 |
26 |
18 |
13 |
12 |
4 |
11 |
6 |
10 |
6 |
6 |
5 |
6 |
14 |
4 |
9 |
Definitions
By section:
- States
-
The bread and butter of a state machine - the states that the machine is
permitted to occupy. It's hard to understand what a state machine that
didn't support states would actually be. On a traffic light, we probably
have red, yellow, green, and off.
- Transitions
-
A machine that supports transitions allows you to specify which state to
move to directly. Almost all machines support transitions. A handful of
machines do not (usually by only supporting actions instead.) These are
often called go, switch, change,
state, set, or assign.
- Actions
-
Actions are things that can be done, from a given state. These are distinct
from transitions, which specify the end goal, by being a label specifying
what's being done, instead; since these are also not part of the input or
output alphabets, these are effectively a layer of indirection on behavior.
What's useful here is the names can be repeated from different starting
points. To progress in our traffic light without actions, we need to know
what color we're on to ask for the successor by title; with actions, we can
just teach each color the idea of next. Actions are sometimes
called tasks, raise, signal,
event, or do. In some machines these are mandatory;
in FSL they are optional.
- Data
-
Data is the difference between a Mealy and a Moore machine - data support
means you can track more than just states. In a vending machine, having no
data means you need states for every valid sum of coin values (one for five
cents, one for ten cents, etc;) having data means you just track
a number for what's already contained. Some machines call this
context, or occasionally input.
- TypeScript data
-
We say the machine supports TypeScript data if the machine's data object
type is customizable, is exposed to TypeScript, and can be enforced by
TypeScript. In this way, the machine's data is fully part of the TypeScript
system.
- General hooks
-
Hooks allow you to specify a function that gets called because of something
that happened. Support for general hooks means that you can establish a
hook on general or global events, such as "any transition" or "any event."
- Specific hooks
-
Support for specific hooks means that you can establish a hook on particular
states, transitions, or actions.
- Post-hooks
-
Posthooks fire after a transition is complete, rather than before, and the
data passed to the posthook reflects the later configuration. If you wanted
to make an editor that visualized states' actions with buttons, you'd need
to use posthooks, not hooks, so that the buttons were for what state they're
now on, rather than the ones in the previous configuration.
- Hook rejection
-
Support for hook rejection means that a given hook is allowed to deny a
given behavior. An example is a state machine representing a user
interface, which has a data member representing whether the user is logged
in, and which disallows switching to the personal profile when not. Hooks
that reject are sometimes called guards, and were called
guards in earlier versions of this machine.
- Transactions
-
In a transactional FSM, everything is transactional - if any hook in a
process rejects, none of the other transformations that would have taken
place do, and everything is rolled back to the end result of the last
successful transition.
- Extending machines
-
Support for extending machines means that an existing machine can be
augmented in place, while keeping its state and any data intact. This is
distinct from changing the source that made a machine and recompiling it;
machine extension works on instances, not definitions.
- Machine composition
-
Machine composition is either the combination of two machines, or the
subordination of one machine to another using internal mechanisms. This is
distinct from putting something together externally using hooks.
- Dynamic graphs
-
In a machine which supports dynamic graphs, the structure of the machine can
be changed while it is running, either in its states, its transitions, or
its actions.
- Properties
-
Support for properties means that states can and may be required to express
named values. This can obviate repetitive switching to make decisions based
on the state outside, and unify the behavior of things depending on machines
under the machines' specification. A traffic light state machine's light
color states might have properties regarding whether you may drive, or
whether to go slowly.
- Methods
-
Support for methods means that states may express named functions. Consider
a state machine representing a network connection, which might be online or
offline; it might express a lookup function which falls back to a local
cache outside the presence of a network connection, but queries a backend
when connected. This feature, when used fully, makes a state machine
equivalent to Strategy Pattern.
- Weighted edges
-
In a machine with weighted edges, transitions can be randomized, and some
probabilities may be stronger than others. This allows machines to directly
model simple probabilities, or probability meshes when used with random
walks. Use of this feature makes a state machine equivalent to a First
Order Markhov Chain.
- Heirarchical states
-
Heirarchical states are a major approach to reducing the number of
transitions in a machine, by allowing them to source from or target groups
of states rather than individual states, frequently reducing a typical
edge count from o(n^2) from state count down towards o(n)
from group count. In an FSM representing a microwave, all states except
idle will have an action for cancel, which could be
reduced to the non-idle heirarchy. A limitation of heirarchies is that they
generally cannot overlap, and groups frequently need to overlap.
- State groups
-
Another method of reducing transition count is to allow the definition of
arbitrary lists of states, and to treat them as heirarchical groups are
treated, as valid source and endpoints. This is slightly more laborious,
but also more flexible, and can be used to implement heirarchical FSMs
directly.
- Timeouts
-
A state with a timeout will, if unchanged and unacted, switch of its own
volition to another state after a specified amount of time. Any transition
or action automatically ends this timer. This is extremely helpful when
implementing protocols, network behavior, enemy agent AI, or timed
element demonstrations.
- Immediates
-
In a machine with support for immediates, after a relevant transition,
action, or hook to a target state, a new transition will automatically occur
to a successor state with no delay. The most common uses for immediates are
merging groups of paths and hooking the groups on the way through, inserting
things into history, coursing during parsing and random construction, or
construction of transfer states for things that wouldn't otherwise be
allowed, such as multiple actions that (eventually) have the same source and
destination states.
- Error hooks
-
An error hook is a hook that's called when an error fires. Errors are
distinct from refusals - asking to switch to a state that isn't allowed, or
one that doesn't exist, are refusals, and should not fire this hook. Errors
are for when you ask for things that don't make sense, such as a string with
an opening quote but not a closing quote. Errors of that form are
relatively rare in finite state machines, but can be important when dealing
with data, dynamic graphs, or combined machines.
- Input/output tape
-
This is the formal classical finite state machine
(Σ,Γ,S,s0,𝛿,F) from the textbooks, which is defined as
two alphabets, one set of transformations, an initial state, and two token
streams. From this worldview on finite state machines, the input alphabet
Σ is the things that are allowed to be on the input tape; the
output alphabet is the your state list by default, but could be changed by
your hooks; the set of states S is just the states you've defined;
the set of transformations 𝛿 is your transitions, accepts an input
symbol (from the tape) if Moore and also some data if Mealy; and
the two streams are the input tape and the output tape. If you'd like to
write a FSM as an acceptor or a validator, typically you would use these
tape facilities. These tend to be found in parsing, iteration, and utility
oriented machines. If you are only using the input tape and a halting state
(by example, a machine that checks if the input is a number,) you create an
acceptor; if you use the output tape to produce a transformed set
of symbols (by example, an upper-casing machine,) you have instead made a
transducer. Support for tape is quite rare, despite being high
value.
- Tape validator
-
A machine with tape validation has API to repeatedly use the same machine
to validate a set of inputs through tape, without making the user implement
the feed machinery repeatedly. These are found almost exclusively in
parsing oriented machines.
- Termination
-
Machine support for termination implies that a machine pays attention to
when a state has no valid exits, frequently offering hooks or callbacks to
let the machine user know that a machine has finalized. This is typically
found in parsing and validation oriented machines.
- Async transitions
-
Asynchronous transitions in machines typically mean that transitions may not
be instantaneous, and that the result of a transition may be a callback,
promise, or generator, instead of an immediately reflected change. This
approach has tradeoffs. On the upside, the number of states being tracked
is often significantly lower, and as such, the transition count quite a bit
lower. On the other hand, this means that state machines may become locked
and unavailable, introducing concurrency concerns, and requiring an api for
mechanisms like is_changing. An alternative approach is to
maintain the instantaneous API, and have states representing things
underway, which is closer to the fundamental nature of an FSM, single
threaded, and more precise, but also more verbose.
- Event emitter
-
An event emitter emits Javascript events for transitions, actions, and so
forth, as a convenient alternative way to notify the outside world besides
hooks. As many Javascript tools consume events, this can remove a lot of
dispatch boilerplate.
- Random walks
-
Random walks allow you to wander over the possibilities in your state
machine. Some state machines, like the canonical weather example, are well
suited to using this directly; in others, this is a great way to validate
that everything in your machine is reachable in a certain depth
(particularly valuable for machines which represent user interfaces.) This
is also frequently a constituent piece of generating state heatmaps.
- Serialization
-
Serialization permits you to take the current state of a machine (with or
without its definition, with or without history, always with data) into a
string format which is safe for storage, and can be reliably unpacked again
later. This is highly useful for save states, database storage, things
moving through queues, and state exchange.
- Factories
-
Factories allow you to create new instances of the same machine with other
configurations quickly and easily, and make it straightforward to map a
container as a set of configurations for new machines, or to treat a machine
specification as a generator. Factories are useful when the same machine
will be used in large numbers. An example would be the people in a game
like Roller Coaster Tycoon - every time a new customer enters the park, the
factory should spin off a new Person with a set of random preferences,
clothes, money, and so forth.
- Named instances
-
When re-using a machine frequently, such as with a factory method or a
generator, it is often useful to name the instances so that you can tell
them apart. By example, this can be useful when making network connections,
parsing files in parallel, or when state machines represent assets in a
system, such as the people and objects in a video game. As the number of
machines you manage grows, so too grows the value of naming instances.
- Automatic API
-
In a machine with an automatic API, transitions and/or actions are
automatically added to the object's method namespace as functions, so that
you don't need to call an indirection like .action('foo'), but
instead just .foo(). This can be complex - one may need a slugging
function, and collisions might become a problem. However, this can also
yield more readable and usable machines, when done skillfully.
Updates
Mistake? Something out of date? New row or column needed?
Please let us know.