Bound functions are one of the most powerful features, that JavaFX Script introduces, but probably also one of the hardest concepts to understand. There are two main use-cases for bound functions:
- The result of the function depends on some external attributes.
- The functions returns an object, which is created in the function. If one of the dependencies changes, the function should not create a new object, but alter the one which was created in the first call.
While I have not encountered the second case so far (but maybe that’s because the concept of bound functions has not fully been absorbed by developers like me). The first case is very common. Consider for example the class in Code Sample 1. (Warning: this is a real world example taken from the current UI-runtime.) 🙂
1 public class CanvasElement { 2 public attribute parentCanvasElement: CanvasElement; 3 4 public function getContainer(): Container { 5 while (p <> null) { 6 if (p instanceof Container) { 7 return p as Container; 8 } 9 p = p.parentCanvasElement; 10 } 11 return null; 12 } 13 }
Code Sample 1: The original function getContainer()
The class CanvasElement is a node in a tree. The function getContainer() returns the closest parent, which is a Container. The result of the function depends on the attribute parentCanvasElement. Evidently, if the node is moved to another place in the tree, the function will return another result. Now, wouldn’t it be nice to make this function a bound function? This would allow us to notify any object that needs to be notified about the new parentContainer without writing a single line of code.
Unfortunately the permitted statements in a bound function are quite strict. Only the definitions of local variables are allowed plus a final expression to return the result of the function. In other words only method-bodies of the following form are allowed:
1 bound function foo() { 2 var i = ...; 3 var j = ...; 4 [...] 5 return result; 6 }
Code Sample 2: The required format of bound functions (informal)
This looks pretty strict at first sight. Consider the function in Code Sample 1. Rewriting the function to match this pattern directly will be hard, if not impossible. But here is a little recipe with which every function can be turned into a legal bound function.
- Take the function-body and create a private function with all dependencies as parameters. (I usually make this function static, so that the compiler warns me, if I forgot a non-static-dependency.)
- Make the original function a bound function and call the function just created, passing all dependencies to it.
By applying this recipe, the bound function follows the allowed syntax. And whenever a dependency changes the result gets reevaluated. Voilá!
Code Sample 3 shows the refactored function getContainer() from the example.
1 public class CanvasElement { 2 public attribute parentCanvasElement: CanvasElement; 3 4 public bound function getContainer(): Container { 5 return getParentContainer(parentCanvasElement); 6 } 7 8 private static function getParentContainer(p: CanvasElement): Container { 9 while (p <> null) { 10 if (p instanceof Container) { 11 return p as Container; 12 } 13 p = p.parentCanvasElement; 14 } 15 return null; 16 } 17 }
Code Sample 3: The refactored and bound function getComponent()