## 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 `$y`and 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
              1.0 $X := true

One can also use an abort command.

### Watching Restrictions

The `whenever` watches variables, not values. This means that the construction monitors the updates of the variables that appear in the logical condition. When a variable is updated, the logical condition is (re)evaluated to decide (if true) to launch the whenever's body.

Additionally, the set of watched variables is determined by a syntactic analysis of the boolean condition. Some systems' variables are not managed as ordinary variables and cannot be watched.

These constraints have several consequences that are reviewed below. Their rationale is to ensure that Antescofo scores remain causal and efficiently implementable.

#### Assignment of a tab

In

            whenever ($t) { ... }
            ; ...
            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.

### 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.

#### Search

From here you can search these documents. Enter your search terms below.