« What an excellent idea... | Main | Griffon and Greet: Starting to get Groovy »

More than I wanted to know about groovy.lang.Closure

Mind you, I'm not going to tell you everything there is to know about Groovy's implementation of Closures. The principal reason being I don't know that much. For the most part I really like Groovy's closures, because they just magically work and I don't have to worry about adding final to externally referenced variables under threat of compilation error. But there comes a time when making a feature work like magic that you just have to know the details of the magical incantation.

It started out innocently enough, with Andres Almiray coming to me at JavaOne 2008 and suggesting the next great idea for the SwingBuilder: make the bind node work with closures. Well, at least well behaved closures. Because sometimes terseness adds more to readability and usability than rigor does. Consider these two lines, which would you prefer to code and read?

label(text: bind(source:model, sourceProperty:'value'))
label(text: bind {model.value})

Certainly, it should be the second line. Immediately my head started to churn, and my first impression was 'is there some way we can make AST Transformations do this?' Because (a) the first beta of 1.6 had just gone out with AST Transformations making it's debut and (b) this seemed like a compile time problem. But quickly things came screeching to a halt. You see the problem is that in Groovy all method calls and property references are dynamic. Meaning that they go through the Meta-Object Protocol (MOP) to dispatch the call or resolve the value of the property. And the actual methods used to resolve these values can be completely altered at runtime (with the right permissions). This presents a problem with compile time transformations: how do you safely do a transformation on a symbol that may not mean what you think it means at compile time? The short answer is you don't. (The longer answer is deserving of another blog post).

So how can I bind to a somewhat arbitrary closure, one who's meaning isn't fully resolved until runtime? The answer is to use the same mechanisms the closure uses at runtime to resolve it's values, but make a mockery of it instead. Err.. I mean.. use the mock object pattern. This is actually where I started to learn more about the incantations of closures than I intended.

In Groovy, each closure creates a separate type, whose name is a mechanical mangling of the owning type: <Class Name>$_run(_closure<number>)+ and each of these types extends groovy.lang.Closure. However, a closure needs to get some access to the methods and properties of the scope in which it was declared and/or in which it is executed. To this end, it has an owner field, which is always set to the instance it was created in, and a delegate field which can be set to any old object. The resolveStrategy property on the closure is then used to determine which of the fields to use and the order to address them in to resolve methods and properties not directly know to the closure. This was simple enough, since I already exploit that knowledge to get any of the builders to work their magic in the first place. However, once I got the simple case working, the real magic of closures made itself manifest: lexically scoped variables. Consider this script:

String bar = "long"
def closure = { [bar.length(), baz.length()] }
bar = "longer than long"
closure.delegate = [bar:"short", baz:"short"]
println closure()

What will the result be? Wait for it.... is it [4, 5], [5, 5], or [16, 5]? The correct answer is, of course the last one. But how can that be? In Java with an inner class I would be forced to declare bar final, and remove the third line since I cannot alter it, so in Java the first answer would almost always be true. But to get the second answer instead of the third one I would have to remove the typedeclaration for bar, and this is the point where I started to see some of the deep magic at work. Whether or not bar is defined as a free variable or a lexically scoped variable is an essential distinction for a Closure. Properties are not lexically scoped variables, and lexically scoped variables are not pushed through the MOP for resolution like properties are. Lexically scoped variables basically have a static meaning, even when used in a closure that may leave scope. (note: the variables themeselves are not subject to the MOP, but the methods or properties on said variable, those are subject to being MOPed up).

So when Groovy compiles a closure and generates bytecodecode it looks over the lexically scoped variables it can see, and compares it against the symbols that the closure is using. When it encounters a variable that is used by the closure it does two things, first it adds to the closure an argument in the constructor, a field, and a getter method to access the variable within the class. Java does this for Inner classes so it is not too shocking. But the second thing it does, which is simple but powerful, is it wraps all access to the variable in the enclosing scope and in the closure inside of a groovy.lang.Reference object. The code looks like it may be accessing an Object on the stack, but the compiler alters those calls for you so you are actually accessing the reference object and using get and set to read and write to the variable. This allows the variable to, in essence, outlive the stack frame it was created in and for other closures to act on the variable in a manner that can be shared across other closures. Consider this slight alteration...

String bar = "long"
def closure = { [bar.length(), baz.length()] }
bar = "longer than long"
def change = {bar = "Doh" }
change()
closure.delegate = [bar:"short", baz:"short"]
println closure() // [3,4]

Providing an alternate value in the delegate still has no effect, but altering it's value inside another closure does affect it. Knowing this I could now create a separate instance of the Closure and fully mock it up against both lexically scoped variables and free variables in an equivalent fashion. The simple act of adding a type to a previously unbound variable will now no longer break things in strange and mysterious ways. Making things work like magic made me learn more than I ever expected to know about how Groovy implements Closures.

Comments (4)

This means you found closure for this issue? ;-)

John Wilson:

It's good practice to clone() the closure and set the delegate on the cloned object unless you are 100% sure that this is the only place in the program that the closure will be used.


Danno Ferrin [TypeKey Profile Page]:

I'm 100% sure that the closure I am generating will only be used to snoop out fields and references. All variables accessible are locally created and none of them are passed outside the snooping methods.

Thanks for posting on this! I was approaching a similar understanding and on searching on the groovy.lang.Reference object I was able to find your explanation.

I've hit a test first impasse, because I want to mock a call to a method with a closure, so I started delving deeper into how they work than I originally wanted to too. Nevertheless, I'm still struggling to find the guts of the closure.

I mean, where might [bar.length(), baz.length()] be in <Class Name>$_run(_closure<number>). I only get as far as the MetaClass where everything is still a little abstract. There must be some representation of the code to be run/resolved in there somewhere!?

Any ideas from your investigations?

Post a comment


About

This page contains a single entry from the blog posted on June 10, 2008 10:02 PM.

The previous post in this blog was What an excellent idea....

The next post in this blog is Griffon and Greet: Starting to get Groovy.

Many more can be found on the main index page or by looking through the archives.

Powered by
Movable Type 3.33