lilypond-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: \once \revert


From: David Kastrup
Subject: Re: \once \revert
Date: Sun, 21 Aug 2011 09:45:49 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.0.50 (gnu/linux)

Carl Sorensen <address@hidden> writes:

> On 8/19/11 2:57 PM, "David Kastrup" <address@hidden> wrote:
>
>> Carl Sorensen <address@hidden> writes:
>> 
>>> On 8/19/11 10:15 AM, "David Kastrup" <address@hidden> wrote:
>>>
>>>> In order to not have the override/revert stack get into unexpected
>>>> interactions, I want to change \once\override to be impervious to
>>>> normal reverts.
>>> 
>>> This seems to me to be a wise decision.  \once \override is a
>>> statement that you are creating an override for everything happening
>>> at the current moment; reverts would not seem to apply.
>> 
>> The main problem is that \once\override comes with its own implicit
>> revert at the end of the time step, and when this implicit revert
>> applies to a different \override, things get surprising.  If this
>> implicit revert is made special so that it will only ever apply to its
>> corresponding \override, then having other \reverts match the
>> \once\override will seem surprising.
>
> Let me see if I can state your concern with an example.
>
> \override x = 1
> \once \override x = 2
> \override x = 3
>
> At the current time step, you want to have x = 3, because of the
> last-encountered override.

Yes.

> Then at the next time step, you want to have x = 3, because of the last
> encountered override.

Correct.

> But if the \once \override x = 2 is canceled by an implicit revert, there is
> the potential to have x = 1 at the next time step, which is a
> counterintuitive and therefore wrong result.

This is actually the current behavior (I expected it to be 2 because
\once would register a revert, but instead it registers setting to the
last value).  In addition, if you then issue another \revert, you'll
likely get x set to 2 afterwards (because \once replaced the last
override with a setting to 1).

> My thought for the architecture is to have two sets of properties -- the
> context set and the \once set.
>
> Let me explain my thought.
>
> Before any of these commands is issued, the context property set has x
> = 4.  The \once set is empty, because there has been no \once issued
> at this time.

Ok.

> when \override x = 1 is received, the context set gets an x=1
> prepended (and put on the stack, I suppose -- I'm not sure exactly how
> this is implemented as a stack).

"prepended" implies stack behavior.  That's all what happens right now.
I used the "stack" verbiage only for describing the semantics of the
alist, not because of a separate implementation.

> When \once \override x=2 is received, the context set gets copied to
> the \once set, because we want to base the \once set on the current
> context set.

Bad mojo.  If you have two copies, you have that because some operations
are easier to do that way.  But you can make actual use of this easiness
only if you allow both copies to get out of sync.  If you allow both
copies to get out of sync, semantics get very hard to predict when you
don't access both copies in a strictly serialized manner (like: don't
use copy 2 as long as copy 1 is still in active use), and if you do, you
don't need another copy in the first place.

> Then x=2 is put on the \once set, but not on the context set.
>
> When \override x=3 is received, it's put on both the \once set and the
> context set.
>
> When it's time to get a property, since the \once set is not null, we get
> the value from the \once set.
>
> At the next time step, we set the \once set back to null, because
> there are no \once overrides.  We don't need an implicit revert; we
> just forget the \once set.

Forgetting the \once set is pretty much what I do currently.

> This comes at the price of carrying a second set of context properties when
> we have a \once.  But that happens very rarely.  So really, we have the cost
> of carrying an empty list around.
>
> Under this architecture, \once means "if the \once set is empty, copy it
> from the current context properties"
>
> \override means "apply the override to the context set.  If the \once set is
> not empty, also apply it to the \once set".
>
> \revert means "apply the revert to the context set.  If the \once set is not
> empty, also apply it to the \once set."
>
> With this architecture, I don't think there are any surprises.

\set x = 4           ; setting the default
\override x = 5      ; context set has 5
\override x = 2      ; context set has 5 2, once set empty, result 5
\once\override x = 3 ; context set has 5 2, once set has 3 5 2, result 3
\revert x            ; context set has 2, once set has 5 2, result 5
[wait ...]           ; context set has 2, once set cleared, result 2

So the \revert caused a change _twice_, first reverting the
\once\override, and then reverting an additional override at the next
time step.  If we view the once stack and the normal stack as one stack
(which they are for the purpose of lookup), you have commands that
manipulate entries at two different stack depths at once.

I count that as a surprise, or at least a complication.  My suggestion
would be

\set x = 4           ; setting the default
\override x = 5      ; context set has 5
\override x = 2      ; context set has 5 2, result 5
\once\override x = 3 ; context set has 3(o) 5 2, result 3
\revert x            ; context set has 3(o) 2, result 3
[wait ...]           ; context set has 2 result 2

Now of course this difference seems rather arbitrary if we have some
linear code playing nonsense with the overrides in a wild order.  The
difference in behavior is then more or less an intellectual game.

In reality, we'll more likely have such overrides occuring in
independent code pieces.

If I have something like
withlargestems = #(define-music-function ...  #{ \override ... \revert #})
and
justonesmallstem = #(define-music-function ... #{ \once\override ... #})
then I want the justonesmallstem to not meddle with the the net stack from
withlargestems even if justonesmallstem occurs at the very last command
of a sequence with large stems.

Anyway, this rationale for a single stack does not yet touch the
question of how to implement the \once\revert semantics that you
considered more logical as my "revert the topmost \once entry" proposal.

>> It is also logically consistent to let the prefix \once make the
>> following operation work on the set of \once overrides, as opposed to
>> the set of not-\once overrides.  And the sets differ by having the
>> \once overrides be autocleared at the end of the timestep.
>
> As described above, I'd have \once work on the \once overrides, but
> I'd have not-\once work on both sets.

Well, let us just say that when I am taxed with writing user-level
documentation exhaustively describing override semantics (exhaustively
meaning that the user does not need to revert to experimentation and
guesswork for more complex cases), I consider myself to be in a les
uncomfortable situation with my approach.

Anyway, there is little point in spending all too much time discussing
semantics when I could just be writing code.  I'll have the privilege of
making the first draft in the manner _I_ consider right, and I intend to
write the code clean enough that others may change it afterwards to
implement different reasonable semantics if enough people agree.

-- 
David Kastrup




reply via email to

[Prev in Thread] Current Thread [Next in Thread]