Re: Lahman, how ya doing?




In article <ciNge.7159$R13.1389@trndny09>,
H. S. Lahman <hsl@xxxxxxxxxxxxxxxxx> wrote:
>Responding to Hansen...

>>>The easy way to do that in the real controller is to put the 420 and 60
>>>tick processing in a lower priority thread. Then the 1 and 7 tick
>>>processing always completes in the triggering tick but the other
>>>processing won't complete until the next tick. Once one gets the
thread
>>>priorities right, everything Just Works.
>>
>>
>> I think I understand that in the context of the real controller. The
high
>> priority stuff gets done first. Then control passes to the low
priority
>> queue. And I suppose that as each low priority event is processed, the
>> time is checked, and if a tick has passed, the low priority queue is
left
>> as it is and another run is done through the high priority queue.
>> (Otherwise, if you complete the low priority queue before returning to
the
>> high priority one, you may as well have a single queue.)
>>
>> But what I don't get is how to translate that to a simulation if
stuffing
>> a
>>
>> timer.add_event(&timer, 420/60, kDelayEvent, kLowPriority);
>>
>> isn't quite what you had in mind. In a simulation, you have to tick
the
>> clock some how.
>
>The difference is that one does not delay the event; it goes out on
>schedule in my solution, just like the hardware clock in the real
>controller. One delays the results selectively so that they emulate the
>delays in the real controller that led to the skipped (deferred)
>processing in a tick. IOW, it is the processing in response to the tick
>event that is prioritized and delayed.

So, uh... it's like a clear queue command? Or it's like a command to a
specific object, "Whatever your new result is, don't tell anyone for two
ticks"?

>
>Aside from faithfully emulating the "witching" ticks in the real
>controller (as I understood them), this keeps Timer's responsibilities
>simple. It just understands the schedule. One implements the delay
>emulation with a second event queue in a different thread with a
>different priority. That isolates those simulation concerns in the
>design so that neither Timer nor the feedback processing need to know
>anything about skipped ticks.

Well, in the real controller, when the processing is delayed, it's resumed
again as soon as possible and with the correct time. E.g. if something is
to occur every 7 ticks the trigger times might look like 7, 14, 22, 28...,
and on that third iteration the call to the appropriate function would
pass 8 ticks rather than 7.

What you seem to be describing in the same scenario is 7, 14, 28..., and
that third iteration would be processed with a time parameter of 7 ticks
rather than 14.

>
>>>My point in this context was that if one needs to emulate those skipped
>>>ticks in the simulation, the easy way to do it is the same way the real
>>>controller does it by putting the processing in a lower level thread
and
>>>tinkering with the priorities. (It's easy because you just need a
>>>second instance of the event queue manager and an adjustment to Timer
to
>>>send the 1 and 7 tick events to one queue instance while pushing the
60
>>>and 420 tick events to the second queue instance.) But that is only
>>>easy if you already have the true event-based infrastructure in the
>>>simulation.
>>>
>>>In contrast, if your simulation implementation is synchronous, then you
>>>have to worry about other things like call graph loops that get back to
>>>Timer, which might suck everything into one thread. Since you are
doing
>>>simulation ticks, that is actually fairly likely because somebody at
the
>>>tail end of the processing has to <synchronously> tell Timer it is time
>>>for another tick. IOw, you may have foot-shooting opportunities
because
>>>the threads don't conveniently terminate like an empty event queue
would.
>>
>>
>> Speaking of telling Timer it is time for another tick, I have to say
I'm
>> not sure I get that, either. Somebody at the tail end of the
processing
>> has to tell Timer it is time for another tick, but nobody should know
that
>> Timer exists? (And actually, the way I've imagined the design, nobody
>> should really know they're at the tail end, either.)
>
>Nobody is supposed to know that you compute temperatures rather than
>polling them from the hardware either. However, there have to be join
>points between the simulation infrastructure and the feedback logic
>processing. So Thermometer is going to have to access the temperature
>values as an attribute rather than from a register as in the real
>controller. That is part of the simulation infrastructure design.
>
>Another join point is invoking Timer.tick. If you don't scale the
>system clock for the simulation, then you need some more direct
>mechanism for letting Timer know it is time for the next tick _in the
>simulation_ and calling Timer.tick will do that. Without scaling that
>will have to be done from the point where you are sure the <necessary>
>tick processing is done. (In the real controller it would be invoked
>from some polling loop for hardware interrupts or OS events.)
>
>However, I only suggested that because Timer.tick() was already in place
>in the discussion. The more elegant way is to make Timer itself have a
>state machine and...
>
>>
>> I can see it in terms of Timer telling Timer it is time for another
tick.
>> E.g. after all objects have been declared, their relationships defined,
>> their order of operations given to timer, the last event to add is
>>
>> timer.add_event(&timer, 1, kTimerTickEvent);
>>
>> Then when control goes to the queue manager to start popping events,
the
>> last one brings us back to Timer.
>
>Very good. This event-based processing stuff isn't that tough after
>all, is it? B-)
>
>Alas, one problem with this solution is that the Timer state machine is
>not very interesting:
>
>[Events Generated] <--------+
> | |
> | Timer Tick Event |
> +-------------------+
>
> From a purist viewpoint object state machines with a single state are
>frowned upon because they don't capture any life cycle constraints in
>their transitions. Often the decision between abstracting the problem
>space in terms of knowledge vs. behavior responsibilities is tough to
>make. One criterion for that distinction is on the basis of whether
>there are constraints on when the object can perform its
>responsibilities. So if there are no constraints or only one
>responsibility, one usually opts for a synchronous knowledge accessor.
>In this case Timer.tick can be regarded as an accessor of the eventList
>data.

I don't know. How do you stop? It seems Timer would have two states, run
and stop. When it counts through the specified number of iterations it
should do something like put a "Go to the next part of the program" event
in the queue.

We've been focusing on going through one cycle of the simulation. But in
a larger problem we could complete a cycle, fit a curve to the data, make
a decision about the next set of parameters to try (e.g. for the
temperature controller), and do it again. Or graph the data and let the
user click little buttons to change parameters. And continue until the
simulated behavior is close to some desired behavior, like a critically
damped response. Then Timer (and other objects) would be repeatedly
reset, started, and stopped.

>
>Another problem from a purist viewpoint is that the Timer Tick Event is
>self-directed (i.e., Timer generates it to itself). That will also tend
>to get the reviewers to haul out crucifixes and garlic cloves. There is
>one camp of state machine design that argues one should /never/ use
>self-directed events because they violate FSA theory. I don't subscribe
>to that because object state machines are not pure FSAs since the
>transition alphabet includes state variables (attributes). Sometimes a
>self-directed event is the simplest and cleanest way to synchronize
>state variables with state machine states. Nonetheless they should be
>used rarely because they often represent a procedural mapping onto OO
>development.

I have read a warning against trying too hard to create a theoretically
ideal program at the expense of a simpler implementation. I think it was
in that UML/OOD book I have. The author has seen much time spent and
complication created by programmers trying harder than they should to do
things the "proper" way.

But I suppose, like a great author breaking grammar rules, you should
first know what the rules are.

What is FSA theory?

>
>FWIW, though, I am with you and I would probably use the single-state
>state machine for Timer. That's because the action (Timer.tick) is
>really about generating events, which is much more dynamic than simply
>accessing eventList knowledge. IOW, I think the nature and importance
>of Timer in the overall simulation design justifies it. OTOH, I would
>also probably look for a logical place to generate the Timer Tick Event
>in some other object. But I wouldn't get too bent over it being
>self-directed if there was some obvious point of generation.
>
>[BTW, there is another potential problem with Timer just generating an
>event for the next tick if one is using a threaded solution for the
>skipped tick simulation. Presumably Timer would be in the high priority
>queue so that it could chug out events while the low priority queue is
>still chugging. The problem is that if the next ticks high priority
>processing gets kicked off immediately, it may "freeze out" the low
>priority thread so that the results skip more than one tick. A related
>problem is that your simulation computations may be so complicated that
>this effect is magnified.

I might try to figure out a way to include a "wait for completion of low
priority queue" event in there. Let's see what you'd do.

>
>Fortunately there is a simple way out, which also makes Timer's state
>machine more interesting, killing two birds with one stone:
>
>[Delayed] <--------------+
> | |
> | Time Out | Timer Tick Event
> | |
> V |
>[Events Generated] ------+
>
>Here the Timer Tick Event triggers the action for [Delayed] that
>requests an OS Time Out event after a ms or so. That inserts a delay
>where Timer is doing nothing and the slow processing gets some cycles.
>When the OS timer signals the elapsed time is over, the Timer
>transitions to [Events Generated] and executes the tick() action.]

That seems like the same general intent that I had, but without flipping
flags.

>>>Alas, while interesting, that isn't what I was looking for. B-) I was
>>>interested in the software implementation (e.g., what
>>>heatsink.connect(...) does, whether link.new_side1 is a behavior or an
>>>attribute, etc.).
>>
>>
>> I guess I don't know the difference between a ehavior or an attribute.
>
>Aha! That's a biggee from an OOA/D viewpoint. In OOA/D knowledge and
>behavior are essentially apples & oranges -- completely different things
>that are handled very differently. So get a six-pack and put your feet
>up on the desk...

Eh, I had some herbal tea, but I finished it. Let me see if I have a
brewski in the fridge... Ah, an MGD and a Wild Goose Porter. Start light
and get to the heavier beers later...

>
>Knowledge and behavior responsibilities are described using different
>object properties. Knowledge is captured in attribute properties, which
>are Abstract Data Types (ADT) that are essentially state variables. But
>because of the ADT nature the attribute may have arbitrarily complex
>structure. (I have seen data structures with ~10**8 individual data
>elements abstracted as a single scalar class attribute.)

I just have to interrupt and say that's a lot of data elements!

>An ADT may
>also have rather complex operations associated with it.
>
>Behaviors are abstracted as method properties that perform some activity
>to satisfy the responsibility. The activity involves some set of rules,
>policies, practices, laws, etc. that exist in some problem space and are
>necessary to solve the problem in hand. Essentially behaviors execute
>problem space rules in the specific problem context.
>
>Alas, the OOPLs tend to confuse these differences in several ways.
>Since the method signature is essentially the message, there is no
>separation of message and method. That's because the OOPLs are all
>still 3GLs and they employ procedural block structuring, stack-based
>flow of control, and procedural message passing. But in OOA/D one
>constructs designs as if messages were completely decoupled from the
>methods that respond to them. (In fact, in UML the interface to a
>class, which describes the messages class members will accept, is a
>different model element from the class definition where the
>responsibilities are defined.)

Are there any candidate 4GLs?

>
>The OOPLs also make no distinction between operations that modify
>knowledge and operations that capture behavior responsibilities. Thus
>an attribute getter looks exactly like a method to execute a behavior.

"Accessor function"?

>This can create confusion when one looks at a class like Matrix. That
>class will have methods for transpose, invert, etc. that are
>algorithmically fairly complex and operate on multiple elements of the
>matrix. However, in most application contexts a Matrix is really just a
>dumb data holder and those operations have nothing to do with the rules
>and policies that are unique the to application problem. They just
>modify the intrinsic data of the Matrix in ways that are defined
>mathematically for much broader scope than the problem in hand.
>
>This segues to another distinction. There are really two sorts of
>behavior /implementations/ that we care about. One implements rules and
>policies that are /unique/ to the specific problem domain. These are
>the ones that we want to be very careful about when we define behavior
>dynamics because they are the constraints that make our solution unique.
> The other sort of implementation deals with rules and policies that
>are not unique to the solution. These, like Matrix, tend to be highly
>reusable, their requirements are unlikely to change, and they depend in
>no way on the specific problem other than parametrically through
>attribute values. We often refer to the second sort as "realized"
>behaviors because they can be encoded entirely outside the OO
>development context. [You may recall I suggested that all of your heat
>flow computations to simulate temperature readings might be done outside
>OO and encapsulated in methods. That's because those calculations are
>exactly this second sort of behavior responsibility.]
>
>In OOA/D we are very careful about that distinction. Operations that
>only modify the object's state variables (data attributes) and whose
>specific does not depend on the specific problem space we capture in
>"synchronous services". (Why synchronous, I will get to in a moment.)
>Such services have a bunch of methodological rules around what they can
>do (e.g., it is a no-no for a synchronous service of one object to
>invoke a synchronous service of another object to access or modify a
>third object's attributes). [In the translation methodologies we go
>even further and insist that all behavior responsibilities be captured
>in object state machines where the methods are state actions. Thus all
>behaviors are state machine actions and all knowledge attributes are
>state variables and knowledge operations are synchronous services.]
>
>Perhaps the main difference between knowledge and behavior is that
>behaviors collaborate in OOA with an asynchronous model because that is
>the most general behavior description. That is so it can be implemented
>as-is with or without concurrency when addressing nonfunctional
>requirements in OOD/P. That asynchronous model is why separation of
>message and method is so important to OOA/D.
>
>However, the assumed arbitrary delay between issuing a message and
>responding to it by executing a method in the asynchronous model makes
>it very tough to deal with data integrity for state variables
>(attributes). One's mind tends to turn to mush trying to ensure timely
>data when arbitrary delays abound. So knowledge is always assumed to be
>accessed synchronously in OOA/D to preserve the developer's mental
health.
>
>[If the implementation is, say, distributed so that access can't be
>synchronous, the OOP implementation must provide infrastructures (e.g.,
>CORBA orbs) to make it appear as if it were synchronous. Most of the
>methodological rules around synchronous services are aimed at making
>that job easier. Developing full code generators for 4GLs tends to give
>one a full appreciation of how important constraints on synchronous
>services are. But the same kinds of problems tend to be encountered
>during maintenance.]
>
>One can argue that much of OO Good Practice -- encapsulation, entity
>abstraction, implementation hiding, separation of message and method,
>peer-to-peer collaboration, flexible logical indivisibility,
>constraining collaboration participation through relationship
>instantiation, and whatnot -- all exist so that the asynchronous view of
>behavior and the synchronous view of knowledge access can play together
>well. [Of course there were a few other objectives, like managing
>global data and eliminating hierarchical implementation dependencies.
>But ain't it great when a Plan comes together? B-) OOA/D represents a
>truly amazing edifice created by getting a few simple notions to play
>together in an elegant fashion. Awesome stuff. But I digress...]
>
>So where does this go? The first major decisions one makes in an OO
>design are deciding whether to abstract the problem space in terms of
>knowledge or behavior. That, in turn, will profoundly affect the way
>one handles the overall flow of control because of the two communication
>models. That's why we have been going around about my event-based
>solution vs. your synchronous solution.
>
>The event-based solution is clearly faithful to the asynchronous OOA/D
>model for behavior communication because one doesn't have a choice with
>an event queue and state machines in the picture. [We translationists
>always use object state machines because they enforce good OOA/D
>methodological practice, not because we were used to them from R-T/E.
>It is not uncommon for the generated code to eliminate the state
>machines for a synchronous implementation.] You will note that when I
>push back on your solution it is usually because of dependencies that I
>perceive could impede maintainability.
>
>In this case...
>
>>
>> The only one I have defined right now is the ChartRecorder, and I stick
a
>> pointer into it when it's initialized,
>>
>> ChartRecorder chart(&heater);
>>
>> That's placed in a private member which I've imaginatively called
>>
>> Object * obj;
>
>No problem. This just instantiates a relationship for subsequent
>collaborations.
>
>>
>> And when an averaging event is sent, that is where it gets its data.
>>
>> void ChartRecorder::evolve(float dt)
>> {
>> num++;
>> float datum = obj->get_output();
>
>Right. The context of collaboration is confident that it is getting to
>exactly the right object. IOW, setting the pointer to the right object
>is a problem that the collaboration context doesn't need to worry about.

I actually wondered if I should generate an event, "send output to chart".
But then I thought that's silly because it's not really an action, it's
just reading an already-computed parameter. It could have been

float datum = obj->output;

reading directly from the variable, except they say that's poor design. I
suppose that's synchronous-- whatever is in there is immediately useable
to anyone that needs it.

>
>> sum += datum;
>> sum2 += datum*datum;
>> tt += dt;
>> }
>>
>> What I'm talking about is making a fairly minor change, e.g. so that
the
>> chart is declared as
>>
>> ChartRecorder chart(&heater, &HeatInput::get_output);
>>
>> And then the member function can be chosen as something other than
>> get_output().
>
>This is potentially stickier. To think about this note that the
>obj->get_output() expression in an OOA/D sense is sending a message
>identified as "get_output" to the object whose address is provided in
>"obj". In principle that is the same thing that Timer.add_event did for
>event messages in eventList; it defined the address and the event
>identifier.
>
>That's fine so long as get_output is a synchronous service, as
><presumably> in this case, or one is only identifying the message to
>send, as in the Timer.add_event case. In both cases one is just
>parameterizing the identification of the message. However, at the OOPL
>level that is not clear because the invocation of get_output is
>synchronous and the OOPL makes no distinction between behavior and
>knowledge accessors.
>
>There are three possibilities for what get_output is:
>
>(A) It is an event identifier, as in the Timer.add_event. In that case
>the event infrastructure would not allow a return value so it is a pure
>message definition. That is, evolve just generates it with no concern
>about what happens as a result. All is well.
>
>(B) It is a knowledge getter, as this case seems to be. It's fair to
>get a value return because knowledge is accessed synchronously. More
>important, the value returned is defined by the public responsibilities
>of the "obj" object (i.e., what values it knows). That is, all one
>needs to know is what the knowledge responsibility is, not how it is
>implemented. In addition, we can be certain due the methodological
>constraints discussed above, that whatever get_output does to produce
>that value, it does not involve any unique rules for the problem
>context. IOW, it just extracts data in a manner that is realized (i.e.,
>invariant with the problem context). All is still well.
>
>(C) It is a behavior responsibility being accessed synchronously. By
>the definition in the discussion above, that behavior enforces problem
>space rules that are unique to the problem in hand. Since such rules
>affect the solution we cannot ignore them in the specification of
>evolve. IOW, the returned value is not just a value the object is
>responsible for knowing; it is produced by executing part of the problem
>solution. That makes evolve dependent on what those rules are. That's
>not so good.
>
>So what's my point? One never gets into the (C) situation if one
>approaches the OOA/D properly by making the distinctions above, using
>the right communication models, thinking in terms of announcement
>messages, and following the methodological rules for forming things like
>synchronous services. However, one may very well get into a situation
>where one needs to parametrically define messages.

I would have to say get_output() is a (B)-- it's a single-line accessor
function {return output;}. (A) is an action taken when an event with an
appropriate ID is sent to the dispatch() member, which returns no value.
It's (C) factored into two different responsibilities.

>
>That may lead to OOP code such as your example. From the code fragment
>itself one cannot tell if the design is well-formed unless one relies on
>things like naming conventions or external documentation. To be sure
>one would have to look into the guts of the implementation of get_output
>and whoever is passing it to evolve. But if one did the OOA/D properly,
>one doesn't need to look; it will Just Be Right.
>
>Bottom line: however stodgy, verbose, unintuitive, etc. the OOA/D
>approach may be, it will make the OOPL code safe for maintainers.
>
>All this because you asked about attributes vs. behaviors! B-)

And I haven't even finished my beer.

--
"A nice adaptation of conditions will make almost any hypothesis agree
with the phenomena. This will please the imagination but does not advance
our knowledge." -- J. Black, 1803.
.