Skip to end of metadata
Go to start of metadata

Closure and Markup name resolution proposal

For the purpose of this proposal lets use the term Markup to refer to Builders and Groovy Markup in general.

Background

Firstly I openly admit and apologise profusely for messing up the Groovy RI and breaking Closures & hacking in Markup semantics into what were meant to be just regular Closures. Currently in Groovy we only have Markup; we don't have Closures. I'd like us to fix that as Closures are important.

Indeed to me at least having proper closure support is way more vital than supporting Markup. Indeed languages like JavaScript and Ruby can implement builder-like functionality without breaking the language or having crazy name scoping rules - I'd far rather descope Markup and have a fixed name scoping rules for closures than to try to fudge Closures and break them by making them be the same thing as Markup.

So what I'd like for 1.0 is for us to have true support for Closures and have support for Markup and use some kinda lexical difference to differentiate the two. I'll explain why we need to differentiate the two concepts, what the benefits of the differentiation are and then propose a syntax to differentiate the two.

We can argue and propose alternative syntaxes to highlight the differences; but I really hope we can all realise while having both Closures and Markup in Groovy really helps lots of users do lots of things; both are very useful.

What are Closures and why are they useful

I'm writing this on a train so I should insert here an external defintion of what a Closure is. My really rough definition is...

  • A closure is a block which is statically bound to its enclosed lexical scope (outer local variables and outer class) which can be passed into methods as a parameter or used as an expression so that the block can be coerced into a kind of object.
  • There is really no difference logically between a closure and a regular block in terms of the body of the code; the naming scope, semantics, meaning of code - other than that the closure is typically passed into some method call or turned into some object which is callable. So the body of Closures are identical to regular Groovy blocks of code.
  • One difference between Closures and regular blocks is that a closure can take parameters that can be passed in when the closure is evaluated; such as to pass in the item being iterated through in a collection.
  • variables declared inside closures and nested closures are still local variables; they are really intended to be just like local variables inside if {} blocks or for {} blocks in Java

A few observations of closures...

  • a closure can be used with .each() and so forth as alternatives to regular Java for statements - there should be no real difference between the two. i.e. the following code is 100% identical when working with closures...

Indeed a smart compiler could optimise the above to inline the closure; to turn the latter code into the same bytecode as the former.

Also a smart compiler could inline this too...

As an end user, interactive shell or IDE; there should be no real difference in the validation rules between code within the closure or code within a regular block of Groovy code.

That is to say that all vanilla names are resolved using the standard Java style naming rules...

Closure name resolution rules == the block name resolution rules which are

  • all vanilla names are first matched against local variables & parameters within scope in the closure & any outer blocks.
  • all names not matching the local variable names (and closure/method parameter names) are matched against the outer classes names.

This allows us to perform static, compile time checking of names against the available names in the outer class along with the lexical context (parameters, local variables etc).

Note that we may have special classes which are 'Maps' or 'Expando' like; they can have arbitrary names in them (e.g. they have a special MetaClass or overload getProperty() or something) - however we should at compile time be able to introspect the type to know if it is a 'closed' type in terms of methods and properties (e.g. regular static typing in Java) or whether its open and can at runtime have any names bound to its instance.

Effects on compile time errors, compile time checking, interactive completion, IDE support

Since closures are really just blocks, regular block name resolution is used; so vanilla names can be checked at compile time/edit time against the static (closed) outer class to know what names are available. This can be used to

  • catch lots of typos - one of the most common causes of bugs & something a programming language - or an IDE - should try and do for us
  • allows IDEs to highlight wrong names before compilation
  • smart completion of names

Note that an 'open' class which is a Map / Expando can have any name and so the compiler/IDE cannot know if a name is invalid until runtime. However the IDE can highlight which names are known to be correct (statically) and which are dynamic. So rather like variables and fields can be highlighted in different colour/font by the IDE - the IDE can highlight dynamic properties from static fields/properties

Summary

So closure are just handy blocks which can be passed around to methods to do useful stuff (like GPath, collection querying and the like).

There's no Closure specific name resolution protocol per se - they are just blocks. So a user who sees a name immediately knows where that name is defined (local scope or outer class); an IDE can easily highlight what names are fields, local variables or dynamic properties etc. The only exception could be the semi-magic 'it' variable.

Markup

Historically markup sections currently looks like a use of regular Closure syntax in the current RI which is wrong since the main change to the Groovy runtime to implement markup was to introduce a new kind of name resolution to simplify Markup. So Markup sections are very different to Blocks and Closures with respect to name resolution.

First some background.

Initially we could use Closures to do markup-like stuff using regular Closure syntax...

So in the above we're just calling markup methods on the builder object, swing. No special changes to the language are required in the above.

However lots folks don't like the 'swing." prefix throughout some markup. Its particularly noisy when doing deeply nested stuff

In the above we kinda wanna change the block code from being regular blocks to being Markup Sections. In a Markup Section, the name resolution stuff changes as we kinda wanna associate vanilla names with the builder object.

For now lets not worry what the exact rules are for what the name resolution rules are - lets just highlight that they are different from regular blocks since within Markup we typically want to associate vanilla names with the builder. The vanilla names are matched first to local variables (& parameters) - however if a name is not a local variable it is matched to the builder first. The builder may use its static names and then delegate back to the outer class maybe or whatever.

So the main point about Markup Sections is that they have a different name resolution mechanism to regular Java & Groovy blocks; the builder is included in the name resolution; which is not the case in Blocks / Closures.

The idea is that since Markup != Closures, we need a way to differentiate the two things as they are entirely different features of a language and should be considered separate semantic things; i.e. the AST needs to figure out the difference between the two concepts so that we can preserve the validation & completion of names in the non-Markup world, while letting the Markup world do whatever it wants.

h3 How to differentiate Markup from Closures

The easy obvious way of doing it is to use prefixes to disambiguate names like the rest of Java and Groovy.

However the noise of all the "swing." code can be offputting for fans of Groovy Markup.

So my proposal is to use a new modified syntax to denote Markup Sections

In the above, with the ".{" section the contents of the block are evaluated such that the vanilla names which don't map to any local variables are all bound with the builder expresion.

So all the markup notation means is that these two blocks of code are 100% identical...

Its just a way avoiding noise.

Note that its easy to escape out of the Markup section using a prefix...

Name scoping rules of Markup Sections

We may have static/closed builders which have a fixed set of names, or we may have dynamic builders which can take any names they like and do something with them.

e.g.

Here the compiler/runtime knows the static type of the bulder and so can inspect the builder class and know what static methods are available on the builder object and if there is no match, the vanilla name can be bound to the outer classes methods. e.g.

For dynamic expressions, we typically have no idea of the type of the builder and so can make no guarrentees over name clashes, we can only do runtime stuff. e.g.

So we say that it is the builders responsibility to figure out what 'frame' means; if the builder can't resolve it we can delegate to the outer class to see if it can; though note a Map or Expando style builder will eat all dynamic names. i.e. in really dynamic expressions, with dynamic (open) classes, the builder will always win - methods on the outer class will be hidden (unless you do wierdness in the metaclass).

So if a user wishes to explicitly and unamiguously refer to names outside of the markup scope, you can use a prefix.

i.e. our general guideline is

  • inside a markup section, vanilla names are typically bound to the builder unless we can know for sure that the builder is not gonna eat up that name - so it kinda depends on the builder
  • an IDE will always know if a name used within a markup section is a local variable, parameter, dynamic property/method of a builder or some statically knowable outer class property/method
  • if we explicitly and unambiguously need to reference outer class stuff we can use the "this." prefix on the vanilla name
  • if you are doing markup where there is little markup going on, but you are using lots of real code within the markup, then it might be an idea to use regular non-markup syntax. e.g.

Summary

So markup syntax really is just a bit of syntax sugar to make writing markup easier when you're mostly doing markup with little 'real' code inside.

Another added bonus of the 'markup syntax' is that you can use it to make construction of objects easier.

So this is the same as

  • No labels