## Reacting to logical events¶

The whenever statement allows the launching of actions conditionally on the occurrence of a logical condition:

          whenever optional_label ( boolean_expression )
{
actions_list
}


The behavior of this construction is the following: The whenever is active from its firing until its end. In absence of an end clause or of an abort command, the whenever will be active until the end of the program execution.

When a whenever statement is active, each time a variable referred to by boolean_expression is updated, the expression is re-evaluated. If the condition evaluates to true, the body of the whenever is launched.

We stress the fact that only the variables that appear explicitly in the boolean condition are tracked. We say that these variables are watched by the whenever.

The boolean expression can be replaced by temporal patterns which ease the specification of complex events over time.

Nota Bene: multiple occurrences of the body of the same whenever may be active simultaneously, as shown by the following example:

              let $cpt := 0 0.5 loop 1 { let$cpt := $cpt + 1 } whenever ($cpt > 0) {
0.5 a₁
0.5 a₂
0.5 a₃
} while ($cpt <= 3)  This example will produce the following schedule: ### Difference between conditional actions and whenever¶ Notice the difference between a conditional action and a whenever: a conditional action is evaluated once when the flow of control reaches the action while the whenever is evaluated as many times as needed to track the changes of the variables appearing in the condition, between its firing and its end. The whenever is a way to reduce and simplify the specification of the score, specifically when actions have to be executed each time some condition is satisfied. It also escapes the sequential nature of traditional scores. The actions resulting from a whenever statement are not statically associated to an event of the performer but dynamically at some point in time where a predicate becomes true. ### The @immediate attribute¶ Note that the boolean condition is usually not evaluated when the whenever is fired because the variables that appears in the whenever are usually not assigned in the same instant. To force the evaluation of the boolean expression when the whenever is fired, one can use the @immediate attribute. This attribute forces the evaluation of the boolean condition when the whenever is fired, in addition to the evaluation caused by an update of the watched variables. Notice that if a watched variable is set in the same instant as the whenever is fired, it does not necessarily trigger the whenever's body, even if the condition is true: The watched variable must be set after the activation of the whenever. For instance  whenever W1 ($y) { print "OK whenever 1 at " $NOW } let$y := true
whenever W2 ($y) { print "OK whenever 2 at "$NOW }
1s
let $y := true  will print  OK whenever 1 at 0 OK whenever 1 at 1 OK whenever 2 at 1  because whenever W1 is triggered two times (at time 0 and time 1) while W2 is triggered only once: at time 0, it is activated after the first assignment of $yand so cannot be triggered.

Instantaneous action that takes place in the same instant are executed sequentially: this is the synchrony hypothesis. The order of execution within an instant depends on the action priority.

### Synchronization Attributes¶

Because the actions in the body of a whenever statement are not bound to an event or another action, synchronization and error handling attributes of the body's instances are the those of the whenever's activation.

### Avoiding Overlapping Instances of a Body¶

The activation of a whenever fires a new group and two such groups may overlap in time. Sometimes it is necessary to avoid this behavior. It can be done using an explicit abort:

          $last_activation := 0 whenever (...) { abort$last_activation
$last_activation :=$MYSELF
; ...
}


which kills the previous instance of the body, if any. The same behavior can be obtained using the @exclusive attribute:

          whenever (...) @exclusive
{
; ...
}


This attribute may also be used on other compound actions. See also section priority for the management of actions that takes place at the same date.

### Stopping a Whenever¶

An end clause can be defined for whenever. These clauses are evaluated each time the logical condition must be evaluated, irrespective of its or value. For example,

          $X := false whenever ($X) { print "OK" $X } during [2 #] 1.0$X := false
1.0 $X := true 1.0$X := true


will print only one OK because at (relative) time $1.0$ the body of the logical condition is false, at time $2.0$ the logical condition is true, the body is launched and then the whenever is stopped because it has been “tested” two times, i.e. [2 #].

Using a duration in relative time or in absolute time gives the a interval of time during which it is active. When the duration is elapsed, the whenever cannot longer fire its body.

The previous example with logical time shows how to stop the whenever after two updates of $X (whatever is the update). It is easy to stop it after a given number of bodies fire, using a counter in the condition: $X := false
$cpt := 0 whenever (($cpt < 1) && $X) {$cpt := $cpt + 1 print "OK"$X
}
1.0 $X := false 1.0$X := true
1.0 $X := true  This will print only one OK at relative time $2.0$. Then the counter is set to 1 and the condition will always be false in the future. However, the previous program will still leave the whenever active: the boolean condition is still checked at each update of $cpt or $X. So its is better to use a logical end clause to terminate the whenever $X := false
$cpt := 0 whenever ($X)
{
$cpt :=$cpt + 1
print "OK" $X } while ($cpt < 1)
1.0 $X := false 1.0$X := true
; ...
let $t[0] := ...  the assigment of a tab element does not trigger the whenever even if the tab is referred by a variable that appears in the whenever condition. As a matter of fact, the value of $t is mutable, the assignment mutates this value but the variable assignation: $t always refers to the same value. #### Reference to a system variable¶ The three system variables $NOW, $MYSELF and $THISOBJ cannot be watched by a whenever.

The variable $NOW appears as continuously updated (there is no notion of quantum step in time progression, so watching this variable amount to execute infinitely often the whenever's body, in a finite time interval). Notice that this is not the case for $RNOW which is updated by discrete jumps at each musical events and meaningful actions.

Variables $MYSELF and $THISOBJ are not real variables: they are constants that denote some exec linked to the context where they appear.

#### Reference to a scoped variable¶

This limitation is rather subtle. Refer to scoped variable to fully appreciate the code below:

        let $g := { @local$x
$x := false whenever U ($x) { print "OK 1" }
10
print "end of G"

}
whenever V ($g.$x) { print "OK 2" }

2 let $g.$x := true  ; [1]


The evaluation of this program will print

     OK 1
end of G


because V does not watch the local variable $x in the group denoted by $g. Only U is triggered by the assignment [1].

As a matter of fact, the variable watched by V is restricted to $g: in the expression $g.$x, $x is only a name, not a variable. The determination of the variable denoted by expression $g.$x is dynamically computed and may change when $g is updated. The set of watched variables is statically determined. Dynamically changing this set is considered too costly and is not managed in the current Antescofo version. ### One Activation per Instant¶ The variables watched by a whenever can be updated several times in the same instant. Howevever, the whenever is fired at most once, with the first update that leads the condition to evaluate to true. For example: $a := false
$b := false$c := false

1
whenever( $a ||$b || $c) { print WHENEVER activated at$NOW $a$b $c } 1$a := false
$b := true$c := true


will print only

    WHENEVER activated at 1.0 false true false


because the wehever is activated at most once per instant, as soon as possible. It is possible to override this behaviour, see sect. Automatic Temporal Shortcut Detection below.

### Causal Score and Temporal Shortcuts¶

The actions triggered when the body of a whenever is fired, may fire other whenever, including itself directly or indirectly. Here is an example:

          let $x := 1 let$y := 1
whenever W1 ($x > 0) { let$y := $y + 1 } whenever W2 ($y > 0)
{
let $x :=$x + 1
}
let $x := 10 @label Start  When action Start is fired, the body W1 of is fired in turn in the same logical instant, which leads to the firing of the body of W2 which triggers W1 again, etc. So we have an infinite loop of computations that are supposed to take place in the same logical instant: $\qquad\qquad$ Start $\rightarrow$ W1 $\rightarrow$ W2 $\rightarrow$ W1 $\rightarrow$ W2 $\rightarrow$ W1 $\rightarrow$ W2 $\rightarrow$ $\quad\dots$ This instantaneous infinite loop is called a temporal shortcut and corresponds to a non causal score. The previous score is non-causal because the variable $y depends instantaneously on the updates of variable $x and variable $x depends instantaneously of the update of the variable $y. The situation would have been much different if the $y assignments had been made after some delay. For example:

          let $x := 1 let$y := 1
whenever W1 ($x > 0) { 1 let$y := $y + 1 } whenever W2 ($y > 0)
{
1 let $x :=$x + 1
}
let $x := 10 @label Start  also generates an infinite stream of computations but with a viable schedule in time. If is fired at $0$, then is fired at the same date but the assignment of will occurs only at date $2$. At this date, the body of is subsequently fired, which leads to the assignment of at date $3$, etc. $\qquad \quad 0:$ Start $\rightarrow$ W1 $\rightarrow$ $\qquad \quad 1:$ $y := 1+1 $\rightarrow$ W2 $\rightarrow$
$\qquad \quad 2:$ $x := 10+1 $\rightarrow$ W1 $\rightarrow$ $\qquad \quad 3:$ $y := 2+1 $\rightarrow$ W2 $\rightarrow$
$\qquad \quad 4:$ $x := 11+1 $\rightarrow$ W1 $\rightarrow$ $\qquad \quad 5: \dots$ #### Automatic Temporal Shortcut Detection¶ Antescofo automatically detects temporal shortcuts and stops the infinite regression. This behavior is a consequence of the rule “whenever are activated at most once per instant” and no warning is issued. Notice that this rule is a sufficient condition to prohibit temporal shortcuts, but it is not a necessary condition. Some times it is desirable to allow several activations of a whenever in the same logical instant, whithout entailing an infinite number of activations. For these (rare) occasion, the attribute @override can be used to bypass the “at most one activation per logical instant” rule. Beware that using this attribute may lead to infinite loops. For example $cpt := 0
whenever ($x) @override {$cpt += 1
}
$x := true$x := true
print \$x


will print 2. Without the @override attribute, the program prints 1.