Open lexuzieel opened 1 year ago
Strongly-typed transition arguments can already be achieved using StateMachine.SetTriggerParameters, see a (probably too short) example here: https://github.com/qmuntal/stateless#parameterised-triggers
If this does not cover your needs I'm open to consider your proposal.
I have read the description of SetTriggerParameters
and although it sort of guards against incorrect type (avoiding panicking with a segfault), however it still panics, from which you have to recover somewhere. Is there a preferred way to handle such panics? Maybe before Fire()
ing an event?
The way I suggest it allows to check arguments when entering a state, linking all error-handling logic to that state. That would allow to put custom logic along-side argument retrieval (i.e. sending bot message with an error to the user) alongside returning Go error
.
If I understand correctly, StateMachine.SetTriggerParameters adds a check that runs when Fire()
is called and panics there before moving on?
If I understand correctly, StateMachine.SetTriggerParameters adds a check that runs when Fire() is called and panics there before moving on?
Yep.
The way I suggest it allows to check arguments when entering a state, linking all error-handling logic to that state. That would allow to put custom logic along-side argument retrieval (i.e. sending bot message with an error to the user) alongside returning Go error.
Please help me understand the use case. Sending a bot message to the user saying "hey, the programmer did not use the correct type when calling Fire(triggetY)
, here is his number: XXX" doesn't seem like the right user experience. I would rather have a recover
which catch any Fire()
panic and sends a generic error to the user.
If you really need to do some state-specific error handling logic around argument types, you can already do so by not using SetTriggerParameters
and just casting the interfaces the usual way.
Currently it is possible to pass arguments using variable number of arguments since
Fire()
function is variadic. This works, however this sort of defeats the purpose of static type checking that Go provides while also bloating user code with type checks.While I have no definite solution in mind, I would like to discuss a better solution for this. Namely, what if we introduced an argument wrapper structure that handled most of typical use cases that users could need.
In my mind I have two specific use-cases:
We could introduce a
stateless.TransitionArguments
struct which would be a wrapper for the underlying[]interface{}
. Then we could tackle both use cases using such wrapper.First use case
For the first use case we could introduce
First()
andEmpty()
methods:Then we could use it like so:
We could further simply it like this:
I am not sure, but maybe there is a way to use reflection to enforce static contract for a value to be either of specified type or
nil
? This could help avoid segfault during runtime.Second use case
For the second use case we could define a fluent API similar to what genv uses.
Of course, this will be a breaking change, but I am interested in what is your opinion on this. There still persists a problem that there is no way to enforce contract for
Fire()
method, but I feel like input validation is much more error-prone and useful in the end.