Boost logo

Boost :

Subject: Re: [boost] New library in Vault: Msm (Meta State Machine)
From: Andreas Huber (ahd6974-spamboostorgtrap_at_[hidden])
Date: 2008-10-18 08:15:28


>> So IIUC then you think that transitions crossing state boundaries are bad
>> practice and should be avoided at all costs?
>
> Yes I do (not at any cost of course). Not only because it looks to me like
> a
> goto, but also because I feel it is a breach of encapsulation, for
> example,
> in the diagram, the "alarm-beeps" state is showing its implementation.

Right, which is why entry and exit points were introduced into the UML
specs. This way, you avoid that a state has to show its implementation while
still allowing state-boundary-crossing transitions.

Whether you'd want to work with entry & exit points in the alarm-beeps case
is a matter of taste. I personally wouldn't bother simply because it seems
unlikely that it can be reused anywhere.

The important point however is that entry and exit points are "just"
abstraction mechanisms. Under the hood you still have a
state-boundary-crossing transition.

> It is
> also unclear to me if the entry method of "alarm-beeps" will be called.

Yes, it must be called, see below.

>> OTOH, the transition triggered by x can only be
>> made non-state-boundary-crossing by introducing a guard, which is IMO
>> less
>> concise than allowing for the transition as shown in the chart.
>
> This is a matter of taste. I personally prefer this as it reduces the
> coupling of the system.

To reach a definite verdict in this particular example we'd need to define
first what coupling in a state machine actually means. To my knowledge,
nobody has done that so far.

>> Given the examples in the Harel paper and the fact that the UML standard
>> expressly allows them, I would encourage you to support them.
>
> So does C++ support goto and breaching encapsulation, and it still doesn't
> make them desirable to me.

Given my first 3 paragraphs above, I hope it has become clear that a
state-boundary-crossing transition combined with an entry/exit-point is
*not* an encapsulation breach.

> Now, I thankfully take the feedback, and if
> confirmed, I will add the entry/exit states.

Just to make sure we're not talking past each other: With "entry/exit
states" I guess you mean "entry point pseudostate" and "exit point
pseudostate" as defined in UML? If not please define.

>> for the same reasons I don't detect value in seeing a composite state as
>> a
>> fully fledged state machine (as your framework seems to do). I know that
>> the UML has so-called submachines but they are really just simple
>> composite states with entry and exit points thrown in.
>
> I admit the UML standard is not too clear in its description of
> submachines
> but states the goal clearly (UML Superstructure specification v2.1.2
> p549),
> as: "Submachine state is a decomposition mechanism that allows factoring
> of
> common behaviors and their reuse".

You have read the whole paragraph, right? I quote it here for clarity:

<quote>
A submachine state is semantically equivalent to a composite state. The
regions of the submachine state machine are the regions of the composite
state. The entry, exit, and behavior actions and internal transitions are
defined as part of the state. Submachine state is a decomposition mechanism
that allows factoring of common behaviors and their reuse.
</quote>

Note the first sentence. What is unclear here?

> And this is a main design point for Msm. Submachines in Msm intend to be
> reusable blocks, like classes and functions. They have entry points (entry
> conditions and initial states),

I don't follow. Isn't it a fact that the current version of Msm only
supports a single entry point for a composite state/submachine, namely the
initial state?

> Now,
> if the outside world knows the inside of the submachine, which would in
> turn know about the outside world, I'm afraid it'll be hard for
> submachines
> to be reused.

Exactly. However, IIUC, at the moment Msm only supports a single entry point
and a single exit point, which IMO also makes reuse harder than necessary,
see below.

> However, your example and your point of view are very interesting as they
> force me to offer a better solution than the one I have (which would be in
> the init state to forward the events manually). Would the following
> compromise solution be acceptable to you? Like with deferred events, it
> would be possible to define automatic forwarding in a state (like the
> initial one) like this:
>
> typedef mpl::vector<T,T1,T2> forward_events;
>
> and at the end of the transition, these events would be resent (to the
> corresponding beep state as defined in the transition table). Like this,
> it
> is guaranteed that the entry methods are called, thus ensuring safe
> initialization and we would come close to an entry pseudostate.

Firstly, let's be clear that any reasonable semantics definition for FSMs
(e.g. Harel, UML) absolutely requires that an entry action of a composite
state/submachine is called when the composite state/submachine is entered
and it doesn't matter at all how the state is entered (ordinary transition,
state-boundary-crossing transition). The same goes for the exit action when
the state is left.

Secondly, IMO your approach with event forwarding makes matters more
complicated than necessary, for at least two reasons (more problems might
surface once you use the approach in practice):
a) The events triggering transitions that enter a submachine (as defined by
UML) by crossing the state boundary must be known inside the submachine.
This undermines reusability, as you can't reuse the submachine in a
different context where other events trigger the transitions. Sure you could
pass the types of the events as template parameters, but you'd still need to
mention the triggering events in two places.
b) Internal events sometimes need to be posted inside transition actions. If
the associated transition happens to be a state-boundary-crossing
transition, then with your proposed approach such an event would need to be
deferred until the forwarded event of the transition has been processed.

To cut a long story short: I would encourage you to do either of the
following:

1) Allow state-boundary-crossing transitions (without entry/exit points).
Once you have these, entry- and exit-points are a no-brainer: An entry point
is simply a member typedef in the type that defines the submachine. An
exit-point can be supported just as easily by passing the destination
state(s) as template parameters to the submachine.
2) Introduce entry & exit points as first-class concepts. Implementing a
state-boundary-crossing transition would then become a three step process:
a) define an entry/exit point, b) connect the point with an internal state,
c) connect the point with an external state.

In my (admittedly biased) view and from a purely conceptual standpoint
(ignoring e.g. performance and other real-world stuff for the moment),
option 1 is clearly superior as it gives the user complete freedom how to
tackle his design problems. Option 2 would be acceptable, when it is clear
that supporting 1) requires too many trade-offs.

Regards,

-- 
Andreas Huber
When replying by private email, please remove the words spam and trap
from the address shown in the header. 

Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk