Groovy Closure Variable Scoping

537 views
Skip to first unread message

Hamlet D'Arcy

unread,
Nov 8, 2007, 10:09:48 PM11/8/07
to Groovy Users of Minnesota
Hi Gang,

I have a question about groovy and variable scoping within closures.

Here is my first example, which prints out 1 2 3 by accessing an
instance variable in the class in which the closure is being executed
(using a 'delegate')

class Utility {
def x = 1

def run = { closure ->
closure.delegate = this
3.times closure
}
}
new Utility().run { println delegate.x++ }

Since x is being reference thru the delegate object, I am able to
increment it within my closure. This is good.

But what I really want is for x to be a local variable inside the run
method, not an instance variable on the class.

I'd like this code to work but it doesn't:
class Utility {
def run = { closure ->
def x = 1
3.times closure
}
}

new Utility().run { println x++ }

at the time println x++ is called, the x variable is not in scope.
Making it delegate doesn't help either b/c I can only reference
instance variables thru the delegate object and not variables on the
local stack.

The typical way to pass this local data into the closure is to use a
parameter, but this doesn't really help me b/c the parameter is by
value, not be reference, so I can't manipulate it. This example
compiles and runs but prints out 1 1 1 instead of 1 2 3

class Utility {
def run = { closure ->
def x = 1
3.times { closure(x) }
}
}

new Utility().run { x -> println x++ }

Any ideas on how I can access a local variable from within my closure
when that local variable is defined in the enclosing method?

Thanks,

Hamlet

Scott Vlaminck

unread,
Nov 9, 2007, 10:06:55 AM11/9/07
to groo...@googlegroups.com
Like you said, it all has to do with closure scoping.

If you defined the closure in the method that called it, I would
expect that to work (but that doesn't really help what you're trying
to do). The only other thing I can think of immediately is to use an
object that holds the int and pass that in, so that you have a
reference to the same variable.

def xHolder = [x:x] // or an object, rather than a map

Scott


--
-------------------------------------------------
Scott Vlaminck // sc...@refactr.com
Refactr LLC // http://refactr.com
mobile // 612-386-9382
-------------------------------------------------

Scott Vlaminck

unread,
Nov 9, 2007, 1:48:42 PM11/9/07
to groo...@googlegroups.com
A full example of the 'holder' idea would be like this (where 'm'
could be any object, not just a map):

class Utility {
def run = { closure ->

def m = [x:1]
3.times { closure(m) }
}
}

new Utility().run {m -> println m.x++ }


I just had another thought as well, although this may still not solve
the problem you are trying to address. Depending on the complexity of
the closure, you could set it's delegate to some other random object,
the example below uses a map.

class Utility {
def run = { closure ->

def closureDelegate = [x:1]
closure.delegate = closureDelegate

3.times closure
}
}

new Utility().run { println delegate.x++ }


Since closures have access to variables in the defining scope (rather
than the executing scope), I thought that it may be possible to work
around this by perhaps creating a temporary closure in the 'run'
method and using it's 'this' or 'delegate', but I tried everything I
could think of and didn't get anywhere more than the above ideas.

Scott

Zan Thrash

unread,
Nov 9, 2007, 2:20:57 PM11/9/07
to groo...@googlegroups.com
One approach that you might want to is the wonderful world of
Curry(ed) Closures. This is some functional programming mojo from the
world of Haskell.

Here is a quick example of how you might address your issue:

class Utility {
def run = { x, closure ->
3.times { closure.call(x++)}
}
}

def myRun = new Utility().run.curry(1)

myRun{ x -> println x }

Hamlet D'Arcy

unread,
Nov 9, 2007, 2:23:36 PM11/9/07
to Groovy Users of Minnesota
Thanks everyone, these examples help a ton!

> > > Refactr LLC //http://refactr.com


> > > mobile // 612-386-9382
> > > -------------------------------------------------
>
> > --
> > -------------------------------------------------
> > Scott Vlaminck // sc...@refactr.com

> > Refactr LLC //http://refactr.com
> > mobile // 612-386-9382
> > -------------------------------------------------

Zan Thrash

unread,
Dec 5, 2007, 2:17:25 PM12/5/07
to groo...@googlegroups.com
I know that this might be a little stale but I was working on my Closure presentation for next week and I started to think about this again.

The issue Hamlet was running into with this code:


class Utility {
 def run = { closure ->
   def x = 1
   3.times { closure(x) }
 }
}

new Utility().run { x -> println x++ }

is that the variable x is being passed as a parameter into the closure, and because it is being pass in by value and not ref the x++ operation has no effect on the variable x.  Effectively the x variable is be bound to the closure each time the looping closure is being executed.

So one way to mitigate this scoping issue is to do the following:


class Utility{
    def run = { closure ->
        def x = 1
        3.times closure       // This is where the difference is

    }
}

new Utility().run { x -> println x++ }

Notice that we are not explicitly passing the locally defined x variable into the passed in closure. Doing this allows the x variable to be with in the lexical scope of the passed in closure; and in turn allows the closure to modify the value of the variable. 

This however kind of breaks encapsulation as your calling code has to be aware of the local variable.
Reply all
Reply to author
Forward
0 new messages