Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

What's the difference between copying and sharing in closure?

0 views
Skip to first unread message

Sam Kong

unread,
Feb 7, 2006, 12:37:54 PM2/7/06
to
Hello!

I read an interview article with matz about closure.
(http://www.artima.com/intv/closures2.html)

He mentioned that Ruby's closure is a real closure like Lisp.
Local variables are shared between a method and a closure.

<snip>
local variables are shared between the closure and the method. It's a
real closure. It's not just a copy.
...
Yes, and that sharing allows you to do some interesting code demos, but
I think it's not that useful in the daily lives of programmers. It
doesn't matter that much. The plain copy, like it's done in Java's
inner classes for example, works in most cases. But in Ruby closures, I
wanted to respect the Lisp culture
</snip>


Can somebody help me understand what he meant?
Closures in other languages are different from Ruby?
And if possible, I want to see the *interesting code demos".

Thanks.

Sam

David Vallner

unread,
Feb 7, 2006, 1:18:09 PM2/7/06
to
Dňa Utorok 07 Február 2006 18:38 Sam Kong napísal:

> Hello!
>
> I read an interview article with matz about closure.
> (http://www.artima.com/intv/closures2.html)
>
> He mentioned that Ruby's closure is a real closure like Lisp.
> Local variables are shared between a method and a closure.
>
> <snip>
> local variables are shared between the closure and the method. It's a
> real closure. It's not just a copy.
> ....

> Yes, and that sharing allows you to do some interesting code demos, but
> I think it's not that useful in the daily lives of programmers. It
> doesn't matter that much. The plain copy, like it's done in Java's
> inner classes for example, works in most cases. But in Ruby closures, I
> wanted to respect the Lisp culture
> </snip>
>
>
> Can somebody help me understand what he meant?
> Closures in other languages are different from Ruby?
> And if possible, I want to see the *interesting code demos".
>
> Thanks.
>
> Sam

A "closure" in Java is somewhat used when doing quick GUI hacks:

class FooButton {
private final String foo = "FOO";
{
this.addActionListener(new ActionListener() {
public actionPerformer(ActionEvent e) {
// Pop up a message box saying "FOO" when the button is pressed.
JOptionPane.showMessageDialog(null, foo);
});
}
}

You can only use variables declared as final inside the anonymous inner class
(the ActionListener); what will be accessible inside the inner class code is
a copy of the reference to foo.

That means you can't assign another String to the variable foo from inside the
"closure" code. Java goes overkill here by making sure you can't possibly
reassign something to foo at all after initialization.

Contrast this to Ruby:

def do_yield()
yield
end

foo = "FOO"
do_yield() {
foo = "BAR"
}
puts foo # Outputs "BAR".

Which means you can assign to local variables in the closure's lexical scope
from inside the closure.

This is usually only really useful when implementing custom control structures
- the #loop method comes to mind. The Smalltalk language made use of this
extensively. Usually, it's much more cleaner and readable to structure your
code so any result of the computation performed inside the block is returned
as (part of) the result of the method you pass a block to - for example using
#collect, #select, or #inject than using #each as you'd use a loop construct
in for example Java.

David Vallner


Dave Cantrell

unread,
Feb 7, 2006, 11:57:55 PM2/7/06
to
> Contrast this to Ruby:
>
> def do_yield()
> yield
> end
>
> foo = "FOO"
> do_yield() {
> foo = "BAR"
> }
> puts foo # Outputs "BAR".
>
> Which means you can assign to local variables in the closure's lexical scope
> from inside the closure.

Question: How is the above different from, say in pseudo code:

function do_something(in)
foo = in
end function

foo = "FOO"
do_something("BAR")

print foo # should still output "BAR", right?

Other than the fact that you can create the block logic at call time
rather than at declaration time, what is the difference in how the two
scopes are handled? In both the Ruby example and the pseudo-code case,
the variable foo is a globally-scoped variable (well, for that code
snippet anyway) and therefore should be accessible to any function that
is subordinate to it.

After reading the example above I was led to try something similar. I'm
definitely a Ruby Nuby (I had to research the IO routines to pad the
code for this e-mail) so I had to fiddle with the variable scoping for a
while to get it to work. It wasn't until I managed to get the variable
assignment into the right place that I got something other than nil or a
proc object.

But in the end, writing this helped me better understand the way Ruby
shares variables in closures. Though I'm still confused as to how it's
truly different from regular scoping rules.

class MethodList
def initialize
@methods = []
end

def add(&block)
@methods << block
end

def run(param)
i = param
@methods.each { |m| i = m.call(i) }
i
end
end

m = MethodList.new
m.add { |x| x + 1 }
m.add { |x| x + 1 }
m.add { |x| x + 1 }

puts m.run(1)


Which, of course, outputs 4. What tripped me up was getting the hang of
the proper way to update the i variable and the proper way to output the
result. I first tried to have the puts in each of the m.add {} blocks --
then I realized I'm overdue for sleep. Doh. Can you see me getting the
hang of blocks? :)

Any clarification greatly appreciated.

Thanks,
-dave


Lionel Thiry

unread,
Feb 8, 2006, 11:31:40 AM2/8/06
to
Sam Kong a écrit :

Sharing closure in ruby:

class A
attr_accessor :a
def initialize
@a = "value"
end
def test
proc { @a }
end
end

a = A.new
block = a.test
puts block.call # "value"
a.a = "changed value"
puts block.call # "changed value"


Copying closure in python:

class A:
def __init__(self):
self.a = "value"
def test(self):
return lambda a=self.a : a

a = A()
block = a.test
print block() # "value"
a.a = "changed value"
print block() # "value"


--
Lionel Thiry

Personal web site: http://users.skynet.be/lthiry/

Jim Weirich

unread,
Feb 8, 2006, 12:06:56 PM2/8/06
to
Sam Kong wrote:
> Can somebody help me understand what he meant?
> Closures in other languages are different from Ruby?
> And if possible, I want to see the *interesting code demos".

Here's an example that happens regularly in testing scenarios:

def test_callback_gets_called
cb_called = false
widget = Widget.new
widget.register_callback { cb_called = true }
widget.do_something
assert_true cb_called, "Expected the callback to be called"
end

Without 'full' closures that can modify the bindings, one would have to
turn cb_called into an object that could be modified, (e.g. an array):

def test_callback_gets_called
cb_called = [ false ]
widget = Widget.new
widget.register_callback { cb_called[0] = true }
widget.do_something
assert_true cb_called[0], "Expected the callback to be called"
end

--
-- Jim Weirich

--
Posted via http://www.ruby-forum.com/.


David Vallner

unread,
Feb 8, 2006, 4:57:31 PM2/8/06
to
Dňa Streda 08 Február 2006 05:57 Dave Cantrell napísal:

> > Contrast this to Ruby:
> >
> > def do_yield()
> > yield
> > end
> >
> > foo = "FOO"
> > do_yield() {
> > foo = "BAR"
> > }
> > puts foo # Outputs "BAR".
> >
> > Which means you can assign to local variables in the closure's lexical
> > scope from inside the closure.
>
> Question: How is the above different from, say in pseudo code:
>
> function do_something(in)
> foo = in
> end function
>
> foo = "FOO"
> do_something("BAR")
>
> print foo # should still output "BAR", right?
>

Wrong. Presuming the pseudocode behaves like ruby with respect to scoping
rules, the "foo = in" in #do_something creates a new local variable foo and
assigns "BAR" to it.

You can of course share a global variable, in Ruby: $foo. The difference with
using a closure is that you share a -local- variable from the enclosing
scope. For a (very contrived) example of where the difference shows:

def do_yield
yield
end

foo = "FOO" # Local variable in the toplevel scope.

def change_and_print_a_foo # *Groan*
# A new local variable in the scope of the method. The toplevel
# variable of the same name cannot be accessed here.


foo = "BAR"
puts foo # Outputs BAR.

do_yield {
foo = "QUUX"
}

puts foo # Outputs QUUX.
end

change_and_print_a_foo

puts foo # Outputs FOO.

This example isn't supposed to demonstrate the usefulness of closures, just
the difference in behaviour when compared to global variables. Possibly, with
LISP-like dynamic scoping, a function could access a local variable from a
calling scope, but to my best knowledge, Ruby is a lexically scoped language.

David Vallner


David Vallner

unread,
Feb 8, 2006, 8:14:47 PM2/8/06
to
I should also really make myself read long blocks of text to avoiding longer
blocks of text.

Dňa Streda 08 Február 2006 05:57 Dave Cantrell napísal:

> In both the Ruby example and the pseudo-code case,
> the variable foo is a globally-scoped variable (well, for that code
> snippet anyway) and therefore should be accessible to any function that
> is subordinate to it.

There's a new term, subordinate functions. But this is essentially the bit
where you blooped up, foo isn't globally scoped. Not even in that code
snippet. Method definitions aren't a closure on their enclosing lexical scope
- they run in a separate scope.

> class MethodList
> def initialize
> @methods = []
> end
>
> def add(&block)
> @methods << block
> end
>
> def run(param)
> i = param
> @methods.each { |m| i = m.call(i) }
> i
> end
> end
>
> m = MethodList.new
> m.add { |x| x + 1 }
> m.add { |x| x + 1 }
> m.add { |x| x + 1 }
>
> puts m.run(1)
>

Arguably a better example of shared lexical closure than mine, but I didn't
feel like hacking up classes. If you can understand why i is four, then
indeed you've pretty much gotten the hang of all there is to it about how
blocks and closures work.

Hmm, I might even rip off your example as a simple stupefactor for people that
don't know Ruby to go along with my "open Integer and then define a
linear-time factorial with Enumerable#inject" one.

David Vallner


David Vallner

unread,
Feb 8, 2006, 8:52:00 PM2/8/06
to
Dňa Štvrtok 09 Február 2006 02:14 David Vallner napísal:

> I should also really make myself read long blocks of text to avoiding
> longer blocks of text.

And write better English. And spam the list less. The above should read "to
avoid writing longer blocks of text", in case anyone cares.


Dave Cantrell

unread,
Feb 8, 2006, 9:28:31 PM2/8/06
to
David Vallner wrote:
> I should also really make myself read long blocks of text to avoiding longer
> blocks of text.
>
> Dňa Streda 08 Február 2006 05:57 Dave Cantrell napísal:
>> In both the Ruby example and the pseudo-code case,
>> the variable foo is a globally-scoped variable (well, for that code
>> snippet anyway) and therefore should be accessible to any function that
>> is subordinate to it.
>
> There's a new term, subordinate functions. But this is essentially the bit
> where you blooped up, foo isn't globally scoped. Not even in that code
> snippet. Method definitions aren't a closure on their enclosing lexical scope
> - they run in a separate scope.

Thanks for taking the time to explain that for me, David.

Yeah, reading back over that I see it sounded kind of weird. I had made
an assumption (I know, I know) that the function declared after the
variable foo was running in the same or subordinate scope as the
variable itself was declared --- whether global or not didn't seem to
matter, but was convenient to write.

So that is definitely where I tripped, because I'm used to dumb
procedural languages (think VBScript and PL/SQL) that work more in that
fashion. I've hacked Python enjoyably, but never fully delved into the
deeper aspects of the language.

Looks like I still have a lot to learn to fully understand Ruby. Luckily
I came to the right place! :)

-dave


0 new messages