< Back to IRCAM Forum

Evaluation Time of Loop Period

Dear JLG, I have another question about the time of evaluations. If I take one of the examples from the documentation, this one:

group LittleTest1 {
    @local $period
    let $period := 1
    loop $period s {
        print Now is $NOW
        0.5 s let $period := $period + 1
    } during [20 s]
}

It prints, as given and expected:

Now is 0
Now is 1
Now is 3
Now is 6
Now is 10
Now is 15

But when I leave out the delay of 0.5 s for changing the period (this is the only difference in LittleTest2), it prints instead:

Now is 0
Now is 2
Now is 5
Now is 9
Now is 14

The actual periods are thus not 1, 2, 3, 4, 5, but instead 2, 3, 4, 5. (In reality, 0 is something like 173.035384, of course.) I assumed that the period is evaluated before entering the body of the loop but this hasn’t proved true. It seems rather that this is done after evaluating and scheduling the action sequence. So, if there are actions without delay, they’re executed before scheduling the next iteration.

This is somewhat counter-intuitive to the position of the period specification at the beginning of the loop construct. It would be helpful to have a hint in the documentation, that the period should be changed inside the loop body only with a delay, be it a very small one, to not interfere with the scheduling of the next iteration.

Or can it be considered good style to begin with a period of 0? E.g.

group LittleTest3 {
    @local $period
    let $period := 0
    loop $period s {
        print Now is $NOW
        let $period := $period + 1
    } during [20 s]
}

At least, it works, printing:

Now is 0
Now is 1
Now is 3
etc.

This style is more or less equivalent to a syntax like

loop {
    let $period := $period + 1
} with [$period s] during [20 s]

which I would consider a little more intuitive (for the innocent composer).

Well, the answer is unfortunately a bit long :slight_smile:

This behavior may seem counterintuitive, but it is designed to make it possible to change the period within the body of the loop. The order of evaluation is what you guessed. More precisely:

  • The first iteration of the loop is scheduled immediately (this is why you have 0 in the two examples).

  • When the body is scheduled at date t, all actions that takes place in the same instant (that, is at date t) are executed. The actions that come after a delay d are simply scheduled at t+d.

  • The period expression is then evaluated. Here the period expression is the variable $period and its value is the value computed at the last assignment.

  • The computed value cv of the period expression is then used to schedule the next iteration of the body at t+cv.

The example in the documentation is not the simplest one, because the date of the computation of the period collides with the date of the assignment of the $period variable. But even if they occur at the same date, they are calculated in a deterministic order (which is known and determined using the action’s priority).

It is possible to change the period in the loop without delay. I think you are disturbed because the first iteration is always launched immediately. This is a wanted feature, not a bug nor a default: when you launch some action, you don’t want the action to wait some delay (if you want to wait some delay, you put this delay before the action and it is not part of the action itself). The behavior is also consistent with the idea of temporal recursion: a construction

    loop period  {  body } 

is equivalent to a recursive processes

   @proc_def ::ploop ()  
    {  
         body 
         period ::ploop()
    } 

(a similar construction translate loops in sequential imperative programming language into a recursive function in a functional programming language).

So, when you write:

can it be considered good style to begin with a period of 0?

the answer is definitively yes, with the proviso that the first period value is then in fact not used at all in this example.

The alternative syntax you propose is interesting but call for two remarks:

  • You have to wait the end of the loop to have an idea of the periodicity of the loop (most loop have a constant period). Which is unfortunate.

  • Your remark implies that you identify the order of execution with the order of apparition in the score. This is true in a first approximation, but not always.

Consecutive actions (without interleaved delay) are executed in the same instant. So their final order of execution depends on their priorities. Priorities in a simple group follows the order of apparition in the program. This order is called the syntactic ordering, and support your intuition that if a precedes b in the score then a is calculated before b.

However, this is not the end of the story. The problem is that an action in a loop is executed several times (one per loop iteration) (and this is also true for action in the body of a whenever which may be instantiated multiple times, in a process or in an object, etc.). Using delays, these occurrence of the same action may collide at the same date. Thus, the syntactic order of apparition in the program is not enough to determine which instance of the action to execute first. For a loop, the iteration number at which the action is scheduled enters in the priority. Some details are given in the chapter Priorities).

I am painfully aware that all these details are a little bit pedantic. But they are the price to pay to maintain a strict determinism in Antescofo computations. And determinism means that you have always the same result in concert (provided you don’t use random functions). Nevertheless, we have tried hard that the semantics of simple programs follows their intuitive meanings. Changing the period of a loop during the loop execution is not really a simple behavior. Yet it is very useful and has a well defined semantics.

Hope it helps.

Thank you very much again, @giavitto, for the in-depth answer. The chapter Priorities is also interesting reading. Antescofo is a powerful language and the underlying concepts are well thought out. And timing is a sophisticated matter!

Changing the loop period within the loop is probably only advisable in rather simple situations. More often than not it should be better to keep the period constant and to eventually change the delays of actions in the loop body instead. Anyway, it’s always important to be aware of the possible interference of actions (and statements and assignments). The tooling to produce interesting things is there…