KStateMachine / kstatemachine

KStateMachine is a powerful Kotlin Multiplatform library with clean DSL syntax for creating complex state machines and statecharts driven by Kotlin Coroutines.
https://kstatemachine.github.io/kstatemachine/
Boost Software License 1.0
358 stars 21 forks source link

Introduce choice state #36

Closed kwasny2 closed 2 years ago

kwasny2 commented 2 years ago

I need to introduce choice state. Only role of such state is to check some conditions and conditionally redirect to another state.

Please check also plant uml documentation: https://plantuml.com/state-diagram#8bd6f7be727fb20e

I know that check can be done in previous state, but unfortunately it is not always good idea. Often such state is some kind of hub where a lot states has transitions to (so logic must be duplicated in many states to avoid check state).

I have also a lot of such situations. I have several blocks of states. One block (which is a also the state) redirects to next block when child state has no transition to next state in the same block.

Sometimes blocks are conditional. So, after some initial check, choice state decides if internal states of block must be processed or to go to next block.

I implemented initial check like that:

fun IState.initialChoiceState(
    name: String? = null,
    block: UnitGuardedTransitionBuilder<Next>.() -> Unit
) {
    var argument: Any? = null
    onEntry {
        argument = it.argument
    }
    initialState("initialChoiceState${hashCode()}<<choice>>") {
        onEntry {
            machine.processEvent(Next, argument)
        }
        transition(name, block)
    }
}

And it used like that (some simplified case):

        addState(RBlock) {

            transition<Next>(targetState = DBlock)

            initialChoiceState {
                guard =
                    { someCheck() }
                targetState = A
            }

            addState(A) {
                transition<Next>(targetState = R)
            }

            addState(R)
        }

        addState(DBlock) {
...
      }

It works OK, but is not the best solution. Internally processEvent is called which typically throws an exception. I need to block the exception like that:

        pendingEventHandler = StateMachine.PendingEventHandler { pendingEvent, _ -> }

Question: could you implement some alternative way to make choice states, without such "hacks"? Sending next event in choice state is something which I want to avoid, just conditionally go to another state.

nsk90 commented 2 years ago

This looks useful, I will see what I can do.

Note that your current implementation with "quite" pending event handler does not seem to be correct. This will mix or drop some notifications. Pending event handler may put event to some kind of queue to be processed after current event processing completes, ignore event, or throw.

nsk90 commented 2 years ago

https://github.com/nsk90/kstatemachine/wiki#conditional-transitions

Currently take a look at this, maybe it may help.

nsk90 commented 2 years ago

Added choice state support in v0.12.0 But there is no "guard" functionality on such pseudo states. Currently it is not possible to use them as initial states. Maybe it will change in future, I am not sure write now.

nsk90 commented 2 years ago

In v0.13.0 kstatemachine allows processing of pending events out of the box.