The bind keyword associates the value of a target variable to the value of a remote variable.
let x = bind someExpression;
This binds the outcome of someExpression
to the variable x
. When someExpression
changes, the value of x
is automatically updated.
The remote variable can be a simple value of some basic type, the outcome of an expression, the outcome of a block of expressions or a bound function.
(The let
keyword introduces a variable that cannot be assigned to).
In some cases you might need to know exactly how the update occurs, or what is
meant by someExpression
changing. When a remote value changes,
a minimal recalculation is always performed. This recalculation only matters in limited circumstances. An example, is when a bind that requires object creation is performed and if, because of object identity, it matters whether a new object was created.
let sum = bind expr1 + expr2;
If expr2
changes sum
is recalculated but expr1
is not. The value for expr1
was previously stored and is fetched again.
The following demo provides an example:
import java.lang.System; var y = 3; function ten() : Integer { 10 } let sum = bind ten() + y; System.out.println(sum); y = 7; System.out.println(sum);
The output of this program is as follows:
13 17
When y
is set to 7, the function ten()
is not
invoked again because it did not change; the value has been remembered and reused.
Within a bind
, the only statements that can occur in a block expression are variable declarations. Note that assignment (including increment and decrement) are prohibited within bind
. Thus a bound block expression has the following form:
bind { var a = expr; var b = expr; var c = expr; expr }
Because any changes to the bound expression cause an update, and because that update is minimal, the variables are effectively bound. Note also that while
, insert
and delete
are expressions and therefore cannot occur within a bind
.
Consider the following expression:
let x = bind if (condExpr) expr1 else expr2;
A change to condExpr
changes which branch of the if
expression is to be evaluated. A change to expr1
or expr2
does not cause either of the other expressions to be recalculated.
Consider the following expression:
let newSeq = bind for (elem in seq) expr;
If seq
changes, the elements in newSeq
that corresponded to elements still in seq
are not recalculated. In other words; if an element is inserted into seq
, the results of applying expr
to that element are inserted into newSeq
at the corresponding positions and the other elements are not recalculated.
The exception to this is that if expr
uses indexof elem
then those elements whose index changed will need to be updated, but corresponding to the minimal update rules.
import java.lang.System; var min = 0; var max = 3; function square(x : Integer) : Integer { x*x } let values = bind for (x in [min..max]) square(x); System.out.println(values); max = 5; System.out.println(values); min = 1; System.out.println(values); min = 0; System.out.println(values);
In this case the following recalculations are performed:
insert
and delete
are used instead.Object literals behave like operators and nonbound functions. If one of the arguments to the object literal changes, it is re-executed (a new instance is created.)
let pt = bind Point { x: myX y: myY }
If myX
changes, the JavaFX™ Script programming language creates a new Point
object -- the expected behavior for immutable objects.
To make the value of x
track the value of myX
without creating a new Point
, binding is required:
let pt = bind Point { x: bind myX y: myY }
For this example, you would probably want to bind y
as well:
let pt = bind Point { x: bind myX y: bind myY }
where pt
would always remain the same Point
instance. It would be the same without the initial bind:
let pt = Point { x: bind myX y: bind myY }
A nonbound function is one that is not preceded with the bound
keyword. For invocations of Java programming language methods or nonbound JavaFX Script programming language functions, the JavaFX Script programming language invokes the function again if any argument changes, but the body of a function is a black box. Dependencies it might have beyond its input parameters do not cause a recalculation.
import java.lang.System; class Point { attribute x : Number; attribute y : Number; } var scale = 1.0; function makePoint(x0 : Number, y0 : Number) : Point { Point { x: x0 * scale y: y0 * scale } } var myX = 3.0; var myY = 3.0; let pt = bind makePoint(myX, myY); System.out.println(pt.x); myX = 10.0; System.out.println(pt.x); scale = 2.0; System.out.println(pt.x);
Output:
3.0 10.0 10.0
Changing the argument myX
causes makePoint
to be called again. But, the function makePoint
is a black box. Changing the value assigned to scale
does not cause an update, as it probably should. The intent of a bound function: It causes the update that you would expect.
Bound functions have as their body a block expression that is bound (it thus has the previous restrictions on bound block expressions). When binding to a bound function occurs, changes besides the arguments cause an update, and the function sees changes to its arguments. The previous makePoint
might have been made a bound function:
bound function makePoint(x0 : Number, y0 : Number) : Point {
If so, the change to the value assigned to scale
would now cause an update (20.0). Note that if myX
changes, only x0 * scale
would be recalculated, not y0 * scale
.
Invoking a bound function from outside a bind is just like invoking a nonbound function.