#631 BoundField/BoundMethod

jodastephen Mon 8 Jun 2009

I've just been looking into whether Fan supports all the key features for properties and I think its lacking one. I also think a related problem affects methods.

Fan currently supports accessing the Field and Method objects by literals:

Person#surname   // the Field instance for surname
Person#toStr     // the Method instance for toStr()
#surname         // used within the Person class
#toStr           // used within the Person class

Both Field and Method have no tie to a specific instance of a Person.

A related operator for this problem is the curry operator:

&toStr           // returns a Func that binds the toStr() method to an instance of Person

Neither of these do what I want.

I want a way to access a bound field/method, which are a key part of fully implemented properties in a language.

What I mean by this is an object that represents the Field and the target object, or the Method and the target object. (Currying doesn't help with methods as you can't get the target object back out as far as I can see).

I'll sketch out an API and syntax to aid discussion:

class BoundField {
  Field field
  Obj target

  Obj? get() { field.get(target) }
  Void set(Obj? value) { field.set(target, value) }
}

class BoundMethod {
  Method method
  Obj target

  Obj? call(Obj? a := null, ...) {...}
  Obj? callList(Obj?[]? args) {...}
}

// syntax sugar for BoundField(Person#surname, myPerson)

// syntax option A
field := Person#surname
boundField := myPerson#surname

// syntax option B
field := Person#surname
boundField := myPerson##surname

// syntax option C
field := Person##surname
boundField := myPerson#surname

// syntax option D
field := Person#surname
boundField := myPerson&surname

Syntax option A requires the existing literal syntax for Field/Method to be changed slightly. Instead of nothing on the left hand side returning a Field, it would return a BoundField using this as the target.

Syntax options B and C use a double # (I'd prefer to avoid this).

Syntax option D uses the curry symbol in a new way. But again, this would really need to change &onChange to be a BoundMethod rather than a curried function. I also suspect that this usage of & cannot be parsed in the grammer.

One further problem is that ideally, BoundField and BoundMethod would be generic. When using this model in applications, you don't always want it to be typesafe access (often Obj is good enough), but sometimes you do. I could live without the generics if it was a way to get the main feture.

The use cases are all around programming with the application in a general way as per reflection. Examples would be processing a field for validation or data binding on a page.

While these two objects can be created easily by applications, they are definitely common enough to justify being in the core. Especially given the need matching literal syntax.

This feature is a missing aspect of Fan for general relective programming, and one that should be fixed for 1.0.

brian Mon 8 Jun 2009

Interesting idea. Although it seems a little obscure to make a full-blown language feature. Effectively it seems you are just trying to bundle up a object reference and a Field into a tuple for convenience?

If we decided to do that, it seems it could be handled with a just an API on Field:

field := #someField.bind(instance)
val := field.get

And unless you were concerned about setter, curry does work for field getters:

Int x := 5
Void main() 
{
  f := &#x.get(this)
  echo(f())
}

Can you give us your thoughts why you think this is common enough to make into a core language feature (or even sys API)? I personally have never had an issue using reflection with just Field myself.

qualidafial Mon 8 Jun 2009

Working on the data binding project at Eclipse, I can definitely see the value of this being part of the language. However looking at something like #x I would not be sure whether it resolves to MyClass#x or this#x. So it may make sense to use a different symbol for bound fields / methods. I like & since this usage seems related to currying.

So these statements would desugar as follows:

a&b   => A#b.bind(a)
&b    => #b.bind(this)

BoundField may be a good place to think about adding change notification support in the future:

a&b.onChange.add |ChangeEvent e|
{
  // handle event
}

qualidafial Mon 8 Jun 2009

Hmm, &b already resolves to #b.curry(this) so maybe we have to pick yet another token.

jodastephen Mon 8 Jun 2009

Interesting idea. Although it seems a little obscure to make a full-blown language feature.

Well, I have a 1.3 MLOC application which heavily uses these in Java (via source code generation) and the concept is core to much of what we do.

Effectively it seems you are just trying to bundle up a object reference and a Field into a tuple for convenience?

Yes, its a bundle of the object reference (not a object reference) and the Field/Method. Why do I emphasise the difference? Because the purpose of the binding is all about providing the context for the property.

And I do explicitly say property. At present, Fan has public fields rather than properties. Full property support is all about that additional binding to the owning object.

Think of it as being each field becoming a fully fledged sub-object of the parent, rather than just being detached meta-data. The way I think of it is that Field/Method are low-level reflection APIs, whereas BoundField/BoundMethod are higher level application APIs (and often referred to as property/operation).

You suggest #surname.bind(myPerson). This sucks IMO, as it doesn't read right (its the surname property of myPerson) and its not type-safe (I can't write an API that only accepts bound properties).

Lets consider a use-case of validating the surname:

// main program
myPerson := readFromInput()
validator.checkCharacters(myPerson#surname)

Or one sketch of a use case for data-binding:

// setup
binder := GuiDataBinder()
binder.bind(surnameTextField, myPerson#surname)

// at some later point:
binder.updateGui()  // updates the gui from the data

My choice of syntax is option A.

Type#slot -> Field/Method
ObjectRef#slot -> BoundField/BoundMethod
#slot -> BoundField/BoundMethod (implied this)

Perhaps BoundField could be a subclass of Field, and BoundMethod a subclass of Method? That would avoid any backwards incompatability.

This feature isn't a new idea in the Java world - Bean properties, Joe Nuxoll (esp. section 2.2) and FCM. C# delegates also have similarities. Basically, its not a new idea, but a natural (and important) completion of Fan's techniques in this area.

tompalmer Tue 9 Jun 2009

#slot -> BoundField/BoundMethod (implied this)

Except I still love the idea of # as a reference to the current type without having to provide the name. Then it doesn't seem bound to this. However, I failed to convince anyone along these lines before. Maybe # by itself just seems too cryptic.

Also, how does this feature relate to fancier binding (with implied events for updates) such as in spreadsheets, Flex, JavaFX, and whatnot?

I'm not against it. Just trying to consider the issues.

brian Tue 9 Jun 2009

I think we start with the API which is really just a tuple of a reference and a Field:

BoundField(this, #field)

Although I think before we added any language sugar, I'd have to be convinced it was really widely used. I've never before wanted something like that, because I tend to use Uris, strings, and dedicated data structures for referencing things.

Although I suspect we will start on some data binding issues for FWT this summer, so let's see how that goes - that is the sort of feature that would shed light on bound fields.

jodastephen Tue 9 Jun 2009

@Tom, Here are the complete set of literals I'm looking for:

Type#        - Type
obj#         - Type of obj (currently obj.type)
#            - Type of this (current class)

Type#slot    - Field/Method
obj#slot     - BoundField/BoundMethod
#slot        - BoundField/BoundMethod of this

Note in particular that obj# has a meaning which is new. I hate using the slot named "type" to do this right now, as "type" is an extremely common field name in business.

(obj# should be a BoundType to make the pattern complete, but a tuple of an object and its type does seem to have limited value unless I've missed something.)

@Brian, An API is a start, but I hope that when you look at the complete set above you'll see how it makes a neat, simple mental model. And I think you'd agree that makes for a good language.

tompalmer Tue 9 Jun 2009

# - Type of this (current class)

I want # for the statically enclosing type, not the type of this, actually. Reason being so I can easily say (and copy-and-paste code saying) things like #.log.error("blah"). Other than logging, it seems like I had another use case for # referencing the static type, but I forget what it was.

tompalmer Tue 9 Jun 2009

I hate using the slot named "type" to do this right now, as "type" is an extremely common field name in business.

I think that's a legitimate concern. But I like it for its current use, too. Even if there is special syntax, I like having some named method for this feature somewhere. Maybe an alternative might be Type.of(obj), but that's a bit bulky, ...

brian Tue 9 Jun 2009

Being able to use just # would be pretty nice. I just have been very conservative about things, so have been sitting on it.

Login or Signup to reply.