Pseudo states

Page contents

Pseudo states are special kind of states that machine cannot enter, but they are useful to describe additional logic in machine behaviour.

Choice state

Choice state allows to select target state depending on some condition. When transition targeting a choice state is triggered, choice function is evaluated and machine goes to resulting state:

class SomeEvent(val value: Int) : Event

choiceState {
    when (event) {
        is SomeEvent -> if (event.value > 3) State1 else State2 // smart-cast to SomeEvent applies in this branch
        else -> { /*...*/ }
    }
}

There is also choiceDataState() function available for choosing between DataStates of same type. You can define dataTransition to target such pseudo data state.

class IntEvent(override val data: Int) : DataEvent<Int>

lateinit var intState1: DataState<Int>
lateinit var intState2: DataState<Int>

createStateMachine(/*...*/) {
    // ...
    val choice = choiceDataState("data choice") {
        val intEvent = event as? IntEvent // cast is necessary as we don't know event type here
        if (intEvent?.data == 42) intState1 else intState2 // attempt of using state of other type will not compile
    }

    // here is a major reason to use choiceDataState(), to specify it as a target of dataTransition()
    dataTransition<IntEvent, Int> { targetState = choice }

    intState1 = dataState<Int>("intState1")
    intState2 = dataState<Int>("intState2")

}

You can use choiceState even on initial state branch. Use initialChoiceState {} / initialChoiceDataState {} as a shorthand when the choice state should also be the initial state of its parent:

createStateMachine(scope) {
    val state1 = state("state1")
    val state2 = state("state2")
    initialChoiceState {
        if (someCondition) state1 else state2
    }
}

Note that choiceState can not be active, so if the library performs a transition and finds that choiceState is going to be activated, it executes its lambda argument and navigates to the resulting state. If the resulting state is also a PseudoState instance, further redirections might be applied.

History state

There are two types of history states, controlled by the historyType parameter:

  • HistoryType.SHALLOW (default) — remembers only the most recently active immediate child; sub-states of that child are not restored (their initial states are used instead).
  • HistoryType.DEEP — remembers the full nested active configuration of the parent, including all sub-states.

You can specify a defaultState which is entered when the history has not been recorded yet (parent was never active). When defaultState is not specified, the parent’s initial state is used.

val machine = createStateMachine(scope) {
    state {
        val state11 = initialState()
        val state12 = state()
        // SHALLOW is the default; use HistoryType.DEEP to restore full nested configuration
        historyState(historyType = HistoryType.SHALLOW, defaultState = state12)
    }
    state {
        // ...
    }
}

This site uses Just the Docs, a documentation theme for Jekyll.