Compound States

Based on the tutorials by @thure.

You might have noticed in the introduction our example included <state> nodes nested inside of other <state> nodes. A <state> node that has child states is known as a compound state.

Compound states are immensely useful. They’re a fantastic means of specifying that a group of states are themselves a state out of which a transition is possible. As in the vampire example: it doesn’t matter if the character is intact, in smithereens, or even in torpor; if the character is burned then the character dies.

The tricky bit is this: if the character were reconstituted from the ashes, what state would he be in? Intact? Torpor? If we were to add <transition event="reconstituted" target="alive"/> inside the dead state, it’s not clear.

There are two ways around this problem: always specify non-compound states (‘simple’ states) as transition targets, or specify an initial state for compound states. If you want your project to scale and you don’t have a specific reason not to point to an initial state, we think you should do the latter.

@initial, <initial>, or first child

The SCXML standard is extremely consistent everywhere but here. Such is the way of a W3C standard, but that’s part of W3C’s strange beauty. You can specify the first sub-state to go to in a compound state in one of three ways:

  • an initial attribute on the compound state, or
  • an <initial> node with a single <transition target="…"> child, or
  • if neither an initial attribute nor <initial> node are specified in the compound state, then the first child state will be the initial state

Here are three statecharts for a microwave that use compound states to describe how the microwave behaves when it’s plugged in:

@initial
<scxml>
  <state id="unplugged">
    <transition event="plug-in" target="plugged-in"/>
  </state>
  <state id="plugged-in">
    <initial>
      <transition target="idle"/>
    </initial>
    <state id="cooking">
      <transition event="stop" target="idle"/>
    </state>
    <state id="idle">
      <transition event="start" target="cooking"/>
    </state>
    <transition event="unplug" target="unplugged"/>
  </state>
</scxml>

Click to show visualization

<initial>
<scxml>
  <state id="unplugged">
    <transition event="plug-in" target="plugged-in"/>
  </state>
  <state id="plugged-in" initial="idle">
    <state id="cooking">
      <transition event="stop" target="idle"/>
    </state>
    <state id="idle">
      <transition event="start" target="cooking"/>
    </state>
    <transition event="unplug" target="unplugged"/>
  </state>
</scxml>

Click to show visualization

First child state
<scxml>
  <state id="unplugged">
    <transition event="plug-in" target="plugged-in"/>
  </state>
  <state id="plugged-in">
    <state id="idle">
      <transition event="start" target="cooking"/>
    </state>
    <state id="cooking">
      <transition event="stop" target="idle"/>
    </state>
    <transition event="unplug" target="unplugged"/>
  </state>
</scxml>

Click to show visualization

Example

This example uses <initial>:

Buttons
Loading:
    I am a state machine
    Microwave

    Click to show source code

    Naturally you wouldn’t want a microwave that’s lost power to just keep cooking the moment it’s plugged in again, so we’ve specified "idle" as the state to go to once it’s plugged in.

    The initial attribute occupies less space and is arguably more straightforward, especially since you’re not actually allowed to do anything else with the <transition> inside <initial>, but as long as you only use one of these two syntaxes you can decide for yourself which to use.

    Using initial or <initial>, you can specify compound interactions that have a default starting point. Later, we’ll introduce a few other ways to jump to a state inside a compound state too, but now you have the tools you need to create a huge range of state machines.

    < Previous Page (Doing with SCXML)Next Page (Conditional Transitions) >