Planet JFX

In attendance: Brian, Per, Lubo, and Robert.

Brian's proposal below for reference.

Consensus was that direct uncontrolled access to attributes is a giant step backwards (malformed objects and lack of encapsulation). Properties are strongly supported.

How this will play with bind and sequences needs to be explored. Preliminary suggestion for bind is an addListener accessor. Interplay with sequences influenced by sequence vs immutable array decision. Insert/delete triggers also a related topic.

Syntax was not discussed, aside from the need for automatic accessor generation.


Brian wrote:

Which brings me to my next point...as I've been trying to write FX library code, I've been frustrated with the fact that its difficult to enforce constraints on attributes. (For examples: there's no equivalent of 'final', which lets an attribute be written only once and enforces it is written exactly once; there's no easy way to reject invalid modifications (perhaps update triggers do this).)

One thing I've wanted to do is to have attributes be readable but not writable. I guess this is why functions are there -- they can be bound to, but can't be modified. But functions are easily confused with operations -- it took me a while to figure out the difference.

It seems that first class properties would make this all a lot cleaner. And, the properties model is familiar to VB/Delphi/etc developers; there is support in both the IDE (objects are configured via property pages) and the language for it. (Properties are sometimes called "smart fields.")

For example, in VB:

Private mAge As Integer
Public Property Get Age() As Integer
  Age = mAge
End Property
Public Property Let Age(ByVal newAge As Integer)
  If newAge >= 0 And newAge <= 130 Then
     mAge = newAge
  Else
     Err.Raise 12345, "MyClass.Age [Let]", "Invalid age"
  End If
End Property

This is ugly and verbose, but the upshot is pretty nice. Externally, it tells clients that this class has a read-write property called Age; clients can read it by referencing foo.Age or set it by saying foo.Age=x. The runtime turns those operations into calls to the Get or Let functions.

Delphi has something very similar:

type
 TMyObject = Class
   private
     class function GetFoo: integer;
     class procedure SetFoo(const Value: integer);
   public
     property Foo : integer read GetFoo write SetFoo;
 end;

If you don't specify a Let function / write function, the property is read-only.


The drawback of property support in these languages is that if you want 'trivial' properties, you still have to write getters and setters, which seems silly.

So, let's say we renamed attributes to properties (this is a term more familiar to the target audience anyway), and added some keywords to enforce things like read-only and required:

class Foo {
    property foo : String;
    readonly property moo : String;
    required property zoo : String;
}

In the absence of getters and setters, the compiler would generate a getter only for readonly properties, and both for non-readonly properties. (For required properties, the compiler would issue an error if an object literal (or constructor) did not set the required properties.)

The generated getters/setters would be adequate for most cases and therefore the code would not look much different than it does now, and this approach doesn't ask the user for much more code (really, the biggest difference is s/attribute/property/). But if the developer wanted to hook into the read/write code paths, he could write explicit getters instead:

operation Foo.getMoo() { ... }
operation Foo.setMoo(String newMoo) { ... }

Computed properties like this eliminate one of the reasons (the last reason?) to have both function and operation -- a point which confuses every new arrival in FX Land. In this scheme, client code could just bind to foo.moo as if it were an ordinary attribute.


Note that there's a lot of noise going on right now over in the Java community about adding properties to Java. Everyone seems to love C# properties. In addition to it being a good idea, I think we'll take crap for almost-but-not-quite having properties in FX.

BTW, here's what properties look like in C#:

public class Person {
    private int theAge;

    public int age {
        get { return theAge; }
        set { theAge = value; }
    }
}


Summary: I make two recommendations here: - generate getters/setters so Java code can safely access FX attributes; - consider first class properties.


Back to OpenJFX Compiler


Comments[]

How do read-only properties help making declarative UI easier to write? I agree that a class writer might want to prevent external change of an internal variable, but the read-only property you are describing sounds a lot more like a function, which is already supported. It might make sense to enrich the syntax such that obj.func really means call the function, in addition to obj.func().

If eliminating the distinction between operations and functions means that functions cannot generally participate in the binding dependency graph, the tradeoff isn't even close to worth it. It might have been a while since I worked in Pascal (quoted above), but I remember it didn't take me long to figure out the difference between procedure and function, and I don't think it takes more than a moment or two to explain the concept now.

Side-effecting operations don't belong in a dependency graph; functions are fine.

OpenLaszlo (which is very instructive in this domain) provides several types of binds, which it terms constraints. They include, in particular "always" and "once". A once constraint sets its target one time, then ignores future updates. Once attributes make for efficient settings of constraint values where changes are not expected until the application or removal of a state (see OpenLaszlo states).