26 Jan 11:45 2005
Builder ideas
<jastrachan@...>
2005-01-26 10:45:40 GMT
2005-01-26 10:45:40 GMT
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 -------