Is it possible to escape names in a closure?

I took the plunge recently and refactored some build script code into an actual Gradle extension. I’m really pleased with the results so far, a lot of clunky imperative code has been replaced by some (perhaps still a little clunky, because I’m just learning) configuration closures.

I have projects.extensions.create('myThingy', ThingyClass, project) near the top of my build script and later I have:

myThingy.config {
  release "1.0"
  apple "green"
  banana "yellow"
}

The config method on myThingy gets passed the closure, I point the closure’s delegate object to myself, add release, apple and banana methods, and I’m off to the races. My config method can use that configuration information to construct a new task and it all works.

So far so good.

As I’m replacing my imperative code with calls to config, I find myself in this situation:

["blue", "green", "teal"].each { release ->
   // stuff
  myThingy.config {
    release "1.0"
}

And it all falls down because release in my closure gets replaced by blue and my closure is suddenly

{ "blue" "1.0" }

Which, unsurprisingly, doesn’t work.

Is there any way to escape the token release in my closure so it remains a method name? Is there some other technique I should be employing? (Am I just doing it all wrong?)

Can you use a different name for the closure argument so that it does not conflict with the method name?

["blue", "green", "teal"].each { color ->
   // stuff
  myThingy.config {
    release "1.0"
}

Or perhaps making the method call explicit might work, release("1.0").

Sure, that’s what I’ve actually done to make progress. But in the general case, it’s inconvenient at best. I’m not the only person who edits the build scripts. If every extension I write that uses a closure unavoidably walks on the variable address space of every context where it’s used, I’m going to be tempted to name the methods thingy_release or the like and that would be … disappointing. I’d much rather be able to say, if you happen to have a variable named release in scope, then put \release "1.0" in the closure (or release("1.0") or any other syntactic change).

But I haven’t found any Groovy escaping for this case. (It may be out there, but searching for “esacaping” returns zillions of pages about escaping characters in strings and the like.)

Making the method call explicit doesn’t work, alas. I sort of assumed that would, but no.

What does your config method look like? I’m mostly interested in what resolution strategy is being used for the closure (I’m thinking it should be DELEGATE_FIRST).

It’s a little complicated, but the config method is just this:

def config(Closure cl) {
    cl.delegate = this
    cl()
   // do some stuff; the methods called by evaluating the
   // closure store stuff in fields that I then operate on here.
}

Calling cl.resolveStrategy = Closure.DELEGATE_FIRST before evaluating the closure didn’t help and setting it to DELEGATE_ONLY had bad consequences for method arguments elsewere.

The only other suggestion I have for you is to also pass the config closure the delegate object so that the users of the config method can use it (or a named argument) in cases of conflict.

def config( Closure cl ) {
    cl.delegate = this
    cl(this)
}

["blue", "green", "teal"].each { release ->
    myThingy.config {
        it.release "1.0"
    }
}

Thanks. The problem isn’t insurmountable, I just assumed I was overlooking something obvious in Groovy.

Simple put the closure parameter is like a method parameter or local variable and thus takes precedence when resolving symbols.

So when searching for something named release without qualifier, the parameter is found and used an as in Groovy you can call anything that has a call method due to its ducktyping, it tries to call what the parameter references.

You would need to do for example delegate.release "1.0" to explicitly qualify which release you mean instead.