Life Codecs @ NamingCrisis.net

Ruminations. Reflections. Refractions. Code.

Aug 16, 2009 - software dev

Groovy Closure and Its Execution Context

I’ve been messing with Groovy – truly – the name aside – it is groovy, and I am impressed.

At some point I’ll say more about it, or dump out various code snippets I’ve been experimenting with to learn it, but for now I wanted to ensure I have this snippet recorded for reference – it is on closures and their context in Groovy. It took me a while to get it. It is always interesting how closures are defined within the context (no pun intended) of the JVM, where functions are not first-class types.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/*
 * For a closure: basically local vars are the ones passed in {x,y,z -> blah}, everything
 * else is picked up and searched for from the execution context. Internally generated as
 * innerclasses with a supplied context consisting of 'this', owner, and delegate (the
 * internal Groovy closure base type is: groovy.lang.Closure -- see its Javadoc, this is
 * what closure blocks get translated to).
 *
 * Method/property resolution order for unqualified methods within a closure is in the
 * following order (and defined by the following context vars available to the closure):
 *
 * 1. this -- execution context - object to which the closure is first bound, e.g. script
 *    or class where defined, NOT the closure instance itself!
 *    
 * 2. owner -- usually equal to this, except when a closure (itself implemented as an
 *    innerclass) then creates an inner closure, that inner closure's 'this' is the outer
 *    closure instance. So that's one way to get access to a closure itself - create a
 *    subclosure and mess with it's 'this'!
 *    
 * 3. delegate -- usually set to owner, but can be modified (others are fixed), for
 *    metaprogramming purposes
 *
 * For better illustration check the following snippet
 */

enclosingInstance = this
outerclosure = {
    // by default, 'this' is always execution context (object enclosing the closure), never this
    // closure instance (which is implemented as groovy.lang.Closure (.doCall))
    assert this == enclosingInstance
    // And so is owner
    assert owner == this
    // this.owner throws a MissingPropertyException, since the outer script has no 'owner' property,
    // and 'this' is the outer script, not the closure instance!
    // But if the closure (outerclosure) creates another closure like so...
    innerclosure = { return this; }
    // The inner closure's owner is the outer closure instance!
    assert innerclosure.owner == outerclosure
    // However the inner closure's 'this' (returned) is still the initial enclosing instance, not the
    // outer closure.
    // Not quite sure if this is intuitive, hmm.
    assert innerclosure() == enclosingInstance
}
// run it!
outerclosure()
// check out it's superclass -- groovy.lang.Closure
println "superclass type: "+outerclosure.getClass().superclass
// done.

Type that out into some file say Foo.groovy and run it (yes you don’t need a static main() method in an enclosing public class with the same filename). The example is focussed on context, it does not provide samples of parameterised closures, or even parameterless ones (technically the closure above has one default parameter called ‘it’ which is null unless otherwise set in the call to the closure :P).