[erlang-questions] gen_statem state enter call and changing t oanother state
Raimo Niskanen
raimo+erlang-questions@REDACTED
Wed Jun 13 10:19:35 CEST 2018
Top posting, for once, since I totally agree!
/ Raimo
On Tue, Jun 12, 2018 at 08:55:58PM -0400, Fred Hebert wrote:
> On 06/12, Raimo Niskanen wrote:
> >Sorry - I do not follow your argument about postponing making sense for
> >"state entered" but not for "entering state"...
> >
> >
>
> Assume I have a state machine with 3 states:
>
> ,--------------------v
> ready -> moving -> stopped-,
> ^-------------------------'
>
> If the enter event is seen as taking place after the state transition
> has taken place, then a transition of the form:
>
> ready -> stopped -> ready
>
> where the transition from 'stopped -> ready' takes place with the
> 'enter' event means that postponing should fire messages enqueued while
> in the 'ready' state even if you're back in the 'ready' state, since you
> went through a successful state transition to a state other than the
> current one.
>
> On the other hand, if the 'enter' event is seen as happening before a
> transition, then the 'enter' event when getting to the 'stopped' state
> would instead be interpreted like:
>
> ready ---x stopped
> |
> '---> ready
>
> Where the enter event _prevents_ switching to the stopped state. In that
> case, then it would make sense not to de-queue events postponed during
> the 'ready' state.
>
> This chain can be as long as you please though and you could imagine
> something like:
>
> a -> b -> c -> b -> c -> b -> a
>
> and if all those moves to 'c' or 'b' take place in 'enter' events, then
> that's an interesting edge case to handle.
>
> >> As for allowing next_event in an enter event, I'm not a fan. If you
> >> really need an event to run as the first thing in an enter event, just
> >> run it in the enter event itself? Otherwise this lets you conceptually
> >
> >You lost me there...
> >
>
> THinking back again, this is not a significant point. You can disregard
> it.
>
> >I decided to have the principal that activating or deactivating
> >state enter calls should not affect event handling in any way.
> >I.e state enter calls should be orthogonal to
> >'postpone' and {next_event, ...}.
> >
> >Therefore you are now forced to insert events from the previous state,
> >and you get the inserted event(s) after the state enter call.
> >
>
> I'm happy with the current state of things.
>
> >
> >I agree. State enter calls were introduced to allow code to be run when a
> >state is entered, without having to duplicate that code at every state exit
> >from the previous state. It is a feature existing in other state machine
> >description languages e.g OpenBSD's ifstated.
> >
> >The implementation now is for the interpretation of "state enter" as
> >"having entered the state", which I think is the safest and least
> >complicated interpretation.
> >
>
> Agreed.
>
> >But the question about the limitations of state enter calls has come up
> >some times now, so maybe it was time to have a second look...
> >
> >The limitations are geared towards state oriented code, in particular
> >callback mode state_functions, so the question is if it would be possible
> >to open up flexibility that would be useful for non state oriented code,
> >without messing up?
> >
>
> I think the limitations should be the same whether you work with
> callbacks or with handle_event(...). They're different types of events.
> I wouldn't expect to use {reply, ..., ...} actions to a cast or internal
> event (an easy one since they don't carry a pid), and I shouldn't expect
> to postpone or next_event action on an enter event.
>
> There's already plenty of context-sensitive information.
>
> >>
> >> Yes, that is an entirely valid concern. I think it is worth making the
> >> existing typespecs a tad more complex to prevent user code from being
> >> even worse than that.
> >
> >Can you elaborate on how to improve the typespecs for this?
> >
>
> I can't, not without repetition. Currently the biggest problem is that
> edoc has you jump around a bit to avoid repetition in the source code.
> For example, if I'm looking at handle_event, I have the following return
> signature:
>
> HandleEventResult
> HandleEventResult = event_handler_result(state())
>
> Jumping to:
>
> event_handler_result(StateType) =
> {next_state, NextState :: StateType, NewData :: data()} |
> {next_state,
> NextState :: StateType,
> NewData :: data(),
> Actions :: [action()] | action()} |
> state_callback_result(action())
>
>
> which has me jump to action():
>
> action() =
> postpone |
> {postpone, Postpone :: postpone()} |
> {next_event,
> EventType :: event_type(),
> EventContent :: term()} |
> enter_action()
>
> which has me jump to enter_action():
>
> enter_action() =
> hibernate |
> {hibernate, Hibernate :: hibernate()} |
> timeout_action() |
> reply_action()
>
> ... and so on.
>
> I basically need to manually click links and jump through at least 3-4
> levels of type definitions to know what I am allowed to return or not.
> Note that I've also skipped over branching, as I have avoided expanding
> state_callback_result(action()), timeout_action(), and reply_action().
>
> By comparison, if the repetition took place in the code rather than the
> doc (or if edoc could expand the type tree), then I could see a single
> signature for the thing.
>
> It totally makes sense for the types to be defined that way in code, but
> they don't make for good documentation.
>
> Regards,
> Fred.
--
/ Raimo Niskanen, Erlang/OTP, Ericsson AB
More information about the erlang-questions
mailing list