qmuntal/stateless

Error control flow poorly documented

soypat opened this issue · 2 comments

After Ctrl+F'ing the go reference page for error handling documentation I can't be sure what happens if an Entry handler returns an error... does the program panic? Does it prevent the state transition and return to previous state (behaviour I would infer)? Some documentation on OnEntryFrom and OnEntry, among others would be nice. Below is an example of what I mean by returning error

SM.Configure(state.TestSystems).OnEntryFrom(trigger.TestSystems, 
        func(c context.Context, i ...interface{}) error {
		act := drivers.FromContext(c)
		if act == nil || act[0] == nil {
			return errors.New("got nil actuator slice in state machine") // what happen?
		}
		setZero(act)
		return nil
	})

TL;DR Agree that the error handling documentation can be improved. I'll work on it for the next release.

Any error created inside trigger actions will be returned in the Fire call that triggered the failing action. panics are only thrown when there is a misconfiguration of the state machine.

There is no rollback mechanism for handling errors after the state has changed. If you want to handle this situations gracefully you can either:

  • Use Permit guards (the third parameter) to deny a transition unless some conditions are meet. Useful for validating inputs before starting a state change.
sm.Configure(stateA).
  Permit(trigger1, stateB, func(c context.Context, i ...interface{}) bool {
    act := drivers.FromContext(c)
    return act != nil && act[0] != nil
  }).
  OnEntry(func(c context.Context, i ...interface{}) error {
    act := drivers.FromContext(c)
    setZero(act)
    return nil
})
  • Model your errors as state changes and handle them using the same state machine. Useful for handling planned error situations.
sm.Configure(stateA).
  OnEntry(func(c context.Context, i ...interface{}) error {
    act := drivers.FromContext(c)
    if act == nil || act[0] == nil {
      sm.Fire(triggerUnexpectedSituation)
      return nil
    }
    setZero(act)
    return nil
})

Maybe I was a bit blunt with the issue. Just like to clarify I absolutely love this library. Thank you for the work you put into it!