How to model a state machine using generics? [dynamic dispatch?]

You cannot handle only valid events, because of StateMachine. To do that you would need type of the event parameter depend on the value of state property. It is not possible in today’s Swift, and even if it would be - I don’t see how it would be usable. It’s just too hard for the client code to proof that event type is correct.

So effectively you need to handle all MxN combinations of (State, Event). Throwing an error counts as handling.

With this mindset, it is pretty straightforward to design a system with runtime checking for validity of events.

You can make states return optional type, with nil indicating invalid transition:

enum Event {
    case start
    case stop
    case receiveConnection(Connection)
}

protocol State {
    func handle(event: Event) -> Optional<any State>
}

struct StateMachine {
    var state: any State

    mutating func handle(event: some Event) throws {
        if let newState = state.handle(event: event) {
            state = newState
        } else {
            print("\(event) invalid for \(state)")
            throw StateMachineError()
    }
}
1 Like