Planet JFX
Advertisement

Currently "functions" differ from "operations" in that they are incrementally reevaluated, that is they: bind the return value, bind the arguments, bind lexically included variables/attributes, and implicitly bind local variables.

The consensus was that having both "operations" and "functions" was confusing. And that automatic binding was confusing and unintentionally overused.

That if it is semantically required to have both bound and unbound procedures, that there should be one name "function", that it should, by default, be unbound and that the needed bindings should be explicitly specified. All felt that bind on local variables should be explicit and that if the return is bound that lexical context is as well. There was not consensus whether a bound function's parameters should be automatically bound or not.

But we felt a meta question needs to be answered: What would break if we don't have bound functions? We still need an answer to the part of this question that is "What would break without incremental evaluation of functions?" (as opposed to wholesale reevaluation); but there is one thing that clearly would break, automatic reevaluation of functions when lexically included variables change. For example:

Class Foo {
   attribute zap; 
   function bar(x) { x + zap } 
} 
var afoo = Foo { zap: 14 } 
var zing = 1; 
var snap = afoo.bar(zing); 
bind dyn = afoo.bar(zing);

Clearly, snap will not change, regardless of changes to zing or zap.

But, we want dyn to change if either zing or zap change. An incremental approach will achieve this but that would require either explicitly or implicitly creating an incrementally evaluated version of bar. Approaches to achieve the lexical dependency were discussed. Per suggested the most promising approach, passing an additional argument to each function which collects the dependencies (and also builds the result) -- this is the approach used in Kawa.

Back to OpenJFX Compiler


Comments[]

It was always reasonably clear to me that functions were the subset of jfx expressions that could be used within bound expressions to return values, and operations were everything else (and specifically set NOT to return a value).

Let me paste here the comment I wrote on Chris' blog, which I think is relevant.

I've spent the last few weeks doing a variety of things with OpenLaszlo, and I'd say that jfx and laszlo have things to learn from each other.

I was a little disturbed by some of the notes on the openjfx compiler wiki - specifically the mention that bindings are "overused". Without a thorough definition by the writers, it's a very big question mark.

If bindings are "overused" because of performance concerns, that is an issue for the jfx runtime, NOT for the language. If they are overused because they are perceived as being difficult to understand, that is an issue with training materials and with the approach to coding.

The whole point of declarative languages is to say what you mean, not how to get there. The more mature a declarative language gets, the more it is able to accomplish its tasks without annoying the user about details that are necessary to derive proper performance. If we want to move user interface creation to a higher plane and make it MUCH easier to do, we need to provide ease of use for mechanisms that actually do that, instead of removing features that appear to be difficult to approach.

In my opinion bindings are _underutilitized_ in jfx, probably because a specific and critical laszlo feature is missing from jfx -- the lz state.

A state packages up behaviors, attribute sets, animations, and even content into a named unit that can be applied to any node, and switched on and off at will. Because they are classes, lz states can get complex in their definitions, and states can have states as well (giving us a nice higher order means of expression). A state is effectively a dynamic mixin to an object.

lz implements them by simply instantiating everything in the state trees but removing them from actually process unless the state apply attribute is true. And the apply attribute on the state is, of course, fully bindable.

To relate this back to animation (and your post), I note that an animation can be placed in an lz state. When the state is applied, all animations in that state are triggered. If a state is removed, all animations are stopped and removed (along with everything else in the state).

So if we have a picture we want to show the user, we can define two different states for that picture -- thumbnail and view. In the thumbnail state we put an event handler (or action) that applies the view state, then removes the thumbnail state. The thumbnail state also has width and height set to small sizes. The view state contains an animation that sets the width and height to the size of the actual image, over some duration.

You're probably familiar with this already, but on the off chance you're not, have a look at openlaszlo. Try to ignore the documentation (which is generally excellent but fails to introduce the declarative stuff early enough), and go straight to states and constraints.

jfx becomes a much stronger language with something like states, and general problem of controlling animation is MUCH easier with them as well.

I've pasted an example before which shows just how useful bindings can be, generally, when you want to start with a set of information, refine it, then display it (possibly in multiple ways)...AND you want it to be automatically maintained.

import javafx.ui.*; 
var numbers = [1,7,4,2,3,1]; 
var odd = bind select n from n in numbers where n % 2 == 1; 
var even = bind select n from n in numbers where n % 2 == 0; 
Frame { 
   title: "Linkage" 
   content: Box { 
       var linking = Label { x: 5, y: 5, width: 100, height: 20, text: bind "# of evens: {sizeof even}" } 
       orientation: VERTICAL 
       border: EmptyBorder { 
           bottom: 5, top: 5, left: 5, right: 5 
       } 
       content:[ 
       linking, 
       Box { 
           var numberButtons = bind select 
           Button { 
               text:"{n}" 
               action: operation() { 
                   insert sizeof numbers as first into numbers; 
               } 
           } 
           from n in odd 
           x: 5, y: 25, width: 500, height: 40 
           content: bind numberButtons 
       } 
       ] 
   } 
}
Advertisement