[erlang-questions] gen_statem state enter call and changing t oanother state

Raimo Niskanen raimo+erlang-questions@REDACTED
Tue Jun 12 10:06:28 CEST 2018

On Mon, Jun 11, 2018 at 12:55:40PM -0400, Fred Hebert wrote:
> On 06/11, Raimo Niskanen wrote:
> >The feature you ask for probably makes more sense for
> >handle_event_function.
> >
> >But there are still some hairy semantics to get right:
> >* If changing states in state_enter call hops more than once - what should
> >  OldState be after the first state_enter call?  I.e how much of a state
> >  change should changing states in the state_enter call be regarded as.
> >  As the gen_statem code looks now it seems to be easier to retain
> >  the original OldState and not regard the state change as complete until
> >  the final state of the state change has been deduced.
> >* If changing states back to the OldState - should postponed events be
> >  retried?  I think not - you in effect stayed in the same state.
> >* Same again: if changing states back to the OldState - should the state
> >  enter call of the OldState be re-run or not?  As the gen_statem code
> >  looks now it seems to be hard to see this as anything else than staying
> >  in the same state hence not running the state_enter call.
> >  Is that intuitive?
> >* Should we also allow {next_event,...} from a state_enter call?
> >* There might be more...
> - Would the state enter represent having entered the state or entering 
>   the state as soon as the callback returns?
>   If it represents having entered the state:
>     - postponing would conceptually make sense, even if it won't in 
>       practice (because you may just forever get back to the postponing 
>       state)
>     - trace/debug calls need to show the state transition as having 
>       happened with no actual events being generated (or rather event a 
>       caused state to change to A, and then state changes B and C 
>       happened with no prompting) -- otherwise you need to consider 
>       state enter events the same way they'd be with external events and 
>       that's even conceptually worse
>     - state timeouts are not ambiguous
>     - if the events entered the state, it does solve the 'OldState' 
>       problem
>   If it does not represent having entered the state
>     - postponing would not conceptually make sense, and won't happen
>     - no hard questions about whether enter state events are special 
>       events or not
>     - state timeouts should possibly not be reset since you have never 
>       left the original state until at the end of evaluating the 'enter' 
>       chain of events
>     - this ends up having the possibility to have a repeated 'OldState' 
>       value, which currently clashes with the expectation that the first 
>       enter event of a FSM is a duplicated one. This would make any 
>       double-transition look like the first transition of the process's 
>       loop.

Sorry - I do not follow your argument about postponing making sense for
"state entered" but not for "entering state"...

I once contemplated having 'enter' as a pure automatically inserted event
then possible to postpone, insert, and so on, but realized the hairiness
that state machines utilizing that would achieve.  So I went with 'enter'
is not an event - it is a special call, so I will have no discussion
about postponing it!

Therefore I want it to be called "state enter call", not anything
implicating "event"...

> It seems that either way, there is a conceptual confusion as soon as an 
> enter state event is allowed to force a change to a new state. I'd 
> rather see the possibility forbidden.
> 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...

> do the same thing as multiple enter-state events with a state transition 
> it seems.

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 think the semantics is hairy enough already, so I still have a bad
> >feeling about this...  But I have asked some colleauages to also
> >contemplate it, we'll see if they can find the time since we are in
> >the middle of the 21.0 release circus.
> >
> Yes. One of the main complaints of gen_statem is that it is a complex 
> behaviour and people take a long while to get adapted. It could be 
> argued that the separation between enter and normal events is already 
> part of that complexity, but in my opinion, not putting these barriers 
> in place actually increase the overall potential system complexity even 
> more.

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.

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 still think gen_statem should have been simpler than it is today, so 
> I'll be vocal about preventing further complexity from being added to 
> it.
> >The only thing that _has_ to be prohibited from a state_enter call is
> >'postpone' since there is no event to postpone.
> >
> >The only reason I have for prohibiting state change and event insertion
> >from state_enter calls is that I am afraid they would get too hairy semantics
> >and hence cause too hard to understand state machines.
> >
> 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?

> Currently the typespecs are too obtuse to be good docs on their own 
> (while they're kind of okay for supervisors and gen_servers), but they 
> represent an intrinsinct complexity warning that a tutorial would have 
> to alert the user to anyway if they were to be simplified. Might as well 
> keep the type complexity and just have the tutorial fix things right 
> away.

I guess you are talking about the Reference Manual that is more or less
built around the type specs, in contrast to the User's Guide (gen_statem
Behaviour decription).

From 21.0-rc2:

Improvement suggestions on both are welcome!

> I'm opposed to adding more conceptual complexity to gen_statem.

I think we are on the same side this time. :-)

> Regards,
> Fred.


/ Raimo Niskanen, Erlang/OTP, Ericsson AB

More information about the erlang-questions mailing list