[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