Wednesday, February 10, 2010

JavaFx: Bound Variables

One of the things that I find most interesting about the JavaFx language is binding: you bind a variable to an expression, and the value of the variable changes whenever the expression changes:

var a = 12;
var b = bind 2 * a;

a = 17;

In this example, the value of b starts out as 24 — 12 * 2. However, when a is updated, the value of b changes: it's now 34.

Binding is implemented using an Observer pattern, rather than lazy evaluation: b is recalculated at the time a is changed (the compiler attaches a “listener list” to all variables involved in a bound expression). Functional purists may be muttering under their breath at this point, but this approach does allow a couple of interesting features.

First, it means that the runtime does not need to completely re-evaluate a bound expression: the compiler memoizes sub-expressions, and only recomputes those that depend on the changed value. This can be useful if the bound expression includes a long-running function:

function longRunningFunction() {
    println("longRunningFunction() invoked");

var a = 12;
var b = bind a * longRunningFunction();

a = 13;

Second, it means that you can create bidirectional binds, in which changing the value of either variable will change the other. This can be useful if you have two linked input fields:

var x = "foo";
var y = bind x with inverse;

println("before change, x = {x}, y = {y}");
y = "bar";
println("after change, x = {x}, y = {y}");

Binding isn't limited to discrete variables. You can pass a bound expression into a class initializer, and that particular instance of the class will be updated as the expression changes:

class MyClass {
    var x : Integer;

var a = 12;
var b = MyClass { x : bind a };
var c = MyClass { x : a };

println("before change: b={b.x}, c={c.x}");
a = 13;
println("after change: b={b.x}, c={c.x}");

When the class is a GUI object, binding becomes a low-effort way to implement MVC: simply bind the relevant fields of the GUI object (view) to the relevant fields of the model object. Having written more than my share of event listeners and active models, this approach seems very civilized.

Binding does, however come with a performance hit: I wouldn't want to bind to a variable that will be updated frequently. And I can imagine a program filled with “spaghetti binds” that are impossible to untangle. It's like any new feature: “with great power comes great responsibility.” I'm sure we'll go through a phase of gratuitous binding, but once programmers get that out of their system we'll have programs that are easier to maintain.

No comments: