26 Jan 2005 11:45
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/
RSS Feed