[PEAK] Re: trellis.Set.discard
Phillip J. Eby
pje at telecommunity.com
Mon Nov 3 14:48:32 EST 2008
At 01:03 PM 11/3/2008 -0500, Phillip J. Eby wrote:
>If we _finish() all cells at the end of this "pre-transaction", then
>cells set as part of initialization will become writable again,
>which is good. Discrete cells (whether rule or value-based) will
>also reset their values, and mark themselves as changed.
>
>By itself, that seems okay and correct. Now let's consider what
>happens if the value was read by a cell within the
>pre-transaction. I think we can establish that this is also safe.
>
>Consider that during a pre-transaction, a new cell's value can only
>read be read by another new cell. This is because by definition,
>the pre-transaction must occur within a single rule execution (or
>non-rule code), and thus only uninitialized rules may be run. (A
>new cell's value can be set, however, by either the calling rule or
>by a new rule.)
>
> From this it follows that the only rules that can be re-run by
> resetting a new discrete cell after the pre-transaction, are rules
> which cannot yet have run in the overall transaction. Thus, the
> cell value reset simply leads to the rule being scheduled to
> execute later within the current transaction -- thereby allowing it
> to correctly see the reset value.
>
>Okay, I think this will work, then. I do need to verify the current
>usage of _finish() does not do anything else that might conflict,
>and then make _finish part of the AbstractCell class (so that e.g.
>Constants don't have problems).
Actually, there's a bit of a problem here. I am conflating new cells
and uninitialized cells here, and calling _finish() on all new cells
- even if they aren't.
For purposes of determining whether a cell should be considered
writable within a read-only state, the newness is what's
relevant. For purposes of determining what to _finish(), however,
it's initialization that's relevant.
As a practical matter, though, the only way in which this distinction
can arise is for a component to create a cell and hold onto it in an
uninitialized state. Then, in some future transaction, when it
initializes, it will _finish() at the end, which is fine.
But what this illustrates is that the idea of _finish()-ing new cells
is doubly wrong: first, it will be called unnecessarily on
uninitialized cells (which isn't really a problem), but also, because
resetting discrete rules at the end of component creation means
you'll never be able to read a discrete rule's first computed value
outside the component that produced it!
So, reading Sergey's previous comment on this:
>Another issue is that discrete rules do not reset before returning
>from component
>instantiation, I don't have a use case where it matters, but it does
>break the promise of independent initialization.
I think I will not use the _finish() hack. Since there is no actual
use case for discrete reset, and I can definitely see use cases for
them *not* resetting, I think that the "promise" we have is the right one.
We are not really promising that rule initialization is
"independent", merely that it does not create conflicts.
I have spent the last hour or so trying to figure out a consistent,
coherent way to allow all of the following:
1. Discrete rules are not reset by component creation (because this
would be bad)
2. Changes made within a component constructor have normal conflict detection
3. Changes made by the rule that invoked a component constructor, do
not conflict with changes made within the constructor
4. An uninitialized rule invoked by a performer is not prohibited
from creating a component
5. Newly-created cells can be modified during a read-only phase (a
compute or perform rule) if and only if a component is being constructed
6. Performers can't modify anything, even a freshly-created component
This is an extremely hard set of requirements to juggle, and after
reflection, it seems to me that #3 is the requirement that causes all
the trouble, and is not really justifiable. There is no reason for a
rule to modify a component it has just created, when it can simply
pass data to the constructor to do that modification.
Therefore, I will not implement that feature. I still need to have
the new-cells tracking to make #4 and #5 work, but #1, #2, and #6
already are correct in the status quo.
What's more, after further reflection, it seems to me that #3 must be
rejected not merely because of implementation difficulty, but rather
because of an inherently flawed assumption embedded in it. The goal
of a trellis application is to be a stable DAG -- that is,
directional and *acyclic*. If you modify a value set by a
newly-created component, then you are putting *input in the output*
-- i.e., reversing the data flow graph, and this is a very bad idea.
What you should be doing is giving the component your input, so that
it can take it into consideration as it builds itself. Otherwise,
you are just hacking something in to fix a broken design.
Whew. Okay, I think I have an action plan now, which begins with
taking out the _finish() hack I just put in, and then removing the
test Sergey sent me for requirement #3. ;-)
More information about the PEAK
mailing list