The syntax for a replace trigger is as follows:
"on" "replace" [oldValue] [ "[" lowIndex ".." highIndex "]" "=" newElements ] block
A replace trigger is invoked after every modification of the variable to which it is attached. The syntactic nonterminals oldValue
,
lowIndex
, highIndex
, and newElements
are effectively formal parameters to a function with a body that consists of the block:
oldValue
is the previous value of the variable, and has the same type as the variable.lowIndex
and highIndex
delimit the portion of oldValue
that has been replaced. Their types are Integer
. For a pure insertion, highIndex==lowIndex-1
.newElements
is the sequence of values that replaces the slice oldValue[lowIndex..highIndex]
. Its type is the same as the variable.The [lowIndex..highIndex]=newElements
parameters are allowed only if the attached variable has sequence type.
Notice the asymmetry between oldValue
and newElements
. oldValue
is the previous value of the attached variable, while newElements
is a slice of the new value of the variable, containing only the modified elements.
The syntax of the replace trigger is meant to be evocative of a slice assignment. For example:
attribute x on replace oldVal[lo..hi]=newVals { exp }; var save = x; x[i..j] = y;
Then exp
is evaluated, with oldVal
bound to save
, lo
bound to i
, hi
bound to j
, and newVals
bound to y
.
Unified replace triggers work well with slice assignments. For example, you can define a bind, as in the following:
attribute x; attribute y = bind x;
as equivalent to:
attribute x = on replace [i..j]=n { y[i..j]=n }; attribute y = [];
If y is a map of x:
attribute x; attribute y = bind for (xi in x) f(xi);
that is equivalent to the following:
attribute x = on replace [i..j]=n { y[i..j] = for (k in n) f(k) }; attribute y = [];
Deleting or replacing a range of elements in a sequence, or inserting a sequence into a single location all result in a single trigger invocation. Some other operations, such as deleting all elements that satisfy a predicate, might be decomposed to multiple trigger call. In that case the state that is seen by each trigger invocation is consistent. Specifically, the programmer-visible state is as if the predicate-delete (for example) were implemented as a set of independent slice-delete operations.