A subtle difference between closures and .& closures in groovy

We were trying to sort out a weird issue we were having in Grails today, and came across a subtle difference in the way groovy deals with closures and closures that are transformed from methods.

In short, when you convert a method into a closure via .&, the references inside the method don’t get converted and you end up with unexpected results.

Let’s say I have the following class

class A{
    def a = { c() }
    def b() { c() }
    def c = { println 'c in A invoked' }
}

It has a closure named a and a method named b. They both call method c.

Let’s say we have another class B,

class B{
    def c = { println 'c in B invoked' }
}

When we take closure a from A and add it to class B, we see that it calls the method c in the new class it has been added into:

def a = new A()
def b = new B()
def m = a.a
b.metaClass.getA = { -> 
    m.delegate = delegate
    m.resolveStrategy = Closure.DELEGATE_FIRST
    m 
}
b.a()

We get the output ‘c in B invoked’, which suggests that our closure is now calling c in the new class it has been transplanted into.

However, if we try to do the same thing by transforming a method via the .& conversion, we don’t get the same results:

m = a.&b
b.metaClass.getB = { -> 
    m.delegate = delegate
    m.resolveStrategy = Closure.DELEGATE_FIRST
    m
}
b.b()

Here, we get the output ‘c in A invoked’. Which is kinda unexpected since we expect that by changing the delegate of our closure, it will invoke the method c() in the class it was transplanted into.

It seems that in the process of converting our method into a closure, groovy somehow keeps track of the original call site and runs this instead of the method in the delegate.

I’m not quite sure if this is a bug or not, but the discrepancy is there between closures and closures converted from methods.

The area that this affects in Grails is dynamic scaffolding, as this technique is used to reconcile and overwrite dynamic scaffolding methods in controllers.

It leads to the bug described in this JIRA. So make sure that whenever you overwrite a method in grails controllers that use dynamic scaffolding, you also overwrite all the methods that use it.

About these ads

4 thoughts on “A subtle difference between closures and .& closures in groovy

  1. Pingback: Questa settimana in Grails (2012-48) - luca-canducci.com - Il blog di Luca Canducci: notizie, tips e nuove tecnologie dal mondo dell’IT.

  2. Pingback: An Army of Solipsists » Blog Archive » This Week in Grails (2012-48)

  3. Pingback: This Week in Grails (2012-48) - Grails Info

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s