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 it is attached to. The syntactic non-terminals oldValue
,
lowIndex
, highIndex
, and newElements
are effectively formal parameters to a function whose body is 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 only allowed if the attached variable has sequence type.
Notice that there is an 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. Given:
attribute x on replace oldVal[lo..hi]=newVals { exp }; var save = x; x[i..j] = y;
Then exp
will be evaluated, with oldVal
bound to save
, lo
bound to i
, hi
bound to j
, and newVals
bound to y
.
Unified replace triggers work great with slice assignments. For example, we can define a bind, as in:
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:
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, may be decomposed to multiple trigger call. In that case the state as seen by each trigger invocation is consistent in the sense that the programmer-visible state is as if the predicate-delete (for example) were implemented as a set of independent slice-delete operations.