jastrachan | 26 Jan 11:45 2005
Picon

Builder ideas

Just a quick mail to describe the builder situation & hopefully prompt 
some feedback. Right now builders are really useful, but there are some 
issues. Its never completely obvious what a name is inside the markup  
- a local variable, method on the class, on the builder etc.

Background
=========

class Foo {
     Object foo() {....}

     void something() {
	bar = {...}

	builder = new MyBuilder()
	builder.whatnot {
	    foo() // is this markup or the foo method?
	    bar() // markup or a local variable closure?
	}
     }
}

So we need some way of handling the name scoping rules of how the 
compiler binds a name to a builder or owner class etc.

The Classic Groovy approach was to make builders normal Groovy syntax; 
which leads to some strangeness; so the closures are not bound to the 
outer class as normal closures are, but they try to bind to the builder 
or if not they bind to the outer class.

Also there are some holes in markup, such as neat namespace support or 
handling of mixed content and so forth.

We spent a while at the JSR meeting going through some different 
possible solutions. The general consensus seems to be that we probably 
need some lexical way to start and stop markup, so that within markup 
we can use different rules to the rest of groovy. This is not unlike 
inside GStrings like  "text... ${....} text..." - as soon as we hit the 
${ we are in a slightly different part of the grammar.

So the idea is we have some way to denote the start/stop of markup, 
then the body looks about the same as before...

<startMarkup>
    foo(a:123)
    html {
	body {
	    p("hello")
	}
     }
     xyz = frame(title:"Swing window") { ... }
<endMarkup>

inside markup things look kinda like groovy; but the idea is all 
non-qualified names (vanilla names) are all on the builder object to 
avoid confusion. Then to refer to names outside of the markup, we 
somehow escape the names. One possible solution is to use qualified 
names to escape out of the builder markup to refer to 
functions/closures/variables/properties defined outside of the markup. 
e.g.

foo() // markup
this.foo() // not markup, we're referring to the foo method on the 
outer class

Markup syntax suggestion
====================

This is still very much a strawman proposal and hasn't been completely 
thought through - so please take this as just a conversation starter. 
At the JSR meeting we came up with a keyword for markup...

with(builder) { .... }

Then the compiler knows the scope of the markup, when it starts & 
stops, so it can apply the 'builder naming rules' within this scope.

Other suggestions have been more operator based, like {{ and }} to 
denote the markup region.

What is a builder anyway?
====================

Currently the builder invokes methods based on the name of the 
'element'. There are a few possible builder implementations we may want 
to do.

(i) the names might match to real methods implemented on the builder. 
e.g. we might have a frame() method no a Swing builder to create a new 
JFrame. Also we might wanna use normal Java beans in markup to build 
graphs of components. Ken Horn suggested it'd be nice to do typesafe 
builders, where compile time errors are generated if you

(ii) sometimes the names are arbitrary strings (think namespaces) and 
for XML style markup, we might want a 'generic' handler interface. e.g. 
something like

nterface Builder {
    void onElement(String elementName, Map arguments, Closure body)
}

so that if you wanted to write a specific marshaller that can handle 
any markup, you could just implement one method (rather than making an 
interceptor of all method invocations).

Type safety
=========

If we went with something like with(builder) and we knew the static 
type of the builder expression, we could warn if bad markup methods are 
done. e.g. if we had a class

class SwingBuilder {
   JFrame frame() { ... }
   JPanel panel() {...}
}

and we wrote code something like

with (new SwingBuilder()) {
    frame {
       pan()  // syntax error, no such method
    }
}

Other ideas
=========

The 'with' idea has been used in other languages as a way to simplify 
operations on things. We could reuse this mechanism to allow simpler, 
more expressive construction of things...

with(new Customer()) {
     name = foo
     address = bar
}

which is equivalent of

tmp = new Customer()
tmp.name = foo
tmp.address = bar

Though we could nest these things maybe?

with(new Customer()) {
     name = foo
     address = with(new Adresss()) {
	city = "Highbury Barn"
	country = "UK"
     }
}

The above is not using any wacky markup stuff per se, just wiring 
together POJOs in a completely typesafe way, but providing a slightly 
more expressive & easy on the eye syntax. So all 'with' is really doing 
is binding vanilla names to the expression, to avoid using a temporary 
variable.

I'm sure others can think of other use cases & issues with builders. So 
in summary, I think we need to figure out

* how do we know when we start/end markup (e.g. the with() syntax)
* what are the exact name scope rules inside markup & how do we escape 
from markup to refer to real methods/variables/fields in lexical scope
* do we like the typesafe builder stuff? e.g. it'd be nice to use 
normal POJOs or factory classes, rather than builder specific classes 
if we can
* is some kinda new Builder interface sufficient for most needs?

Thoughts?

James
-------
http://radio.weblogs.com/0112098/


Gmane