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
will be 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 may need to know exactly how the update occurs, or what is
meant by someExpression
changing. When a remote value changes,
a minimal recalculation will always be performed. This only matters in limited circumstances; for example, when a bind that requires object creation is performed and if, because of object identity, it matters whether or not a new object was created.
let sum = bind expr1 + expr2;
if expr2
changes then sum
will be recalculated but expr1
will not. The value for expr1
was previously stored and will simply be re-fetched.
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:
13 17
When y
is set to 7, the function ten()
is not
invoked again since it did not change; its value has been remembered and reused.
Within a bind
, the only statements which 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 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, it is easy to see that the variables are effectively bound. Note also that while
, insert
and delete
are statements and therefore can not 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
statement will 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
which 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 non-bound functions. If one of the arguments to the object literal changes then 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 will create 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
, then 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 non-bound function is one that is not preceded with the bound
keyword. For invocations of Java programming language methods or non-bound JavaFX Script programming language functions, the JavaFX Script programming language re-invokes the function if any of the arguments change, 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
will not cause an update, as it probably should. That's where a bound function is intended to work -- a bound function causes the update that you would expect.
Bound functions have as their body a block-expression which is bound (it thus has the above restrictions on bound block-expressions). When binding to a bound function, changes besides the arguments causes an update, and the function sees changes to its arguments. If the above function makePoint
were made a bound function:
bound function makePoint(x0 : Number, y0 : Number) : Point {
The change to the value assigned to scale
now causes 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 invokling a non-bound function.