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

S

Sam Kong

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
 
D

David Vallner

D=C5=88a Utorok 07 Febru=C3=A1r 2006 18:38 Sam Kong nap=C3=ADsal:
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 =3D "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 cla=
ss=20
(the ActionListener); what will be accessible inside the inner class code i=
s=20
a copy of the reference to foo.

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

Contrast this to Ruby:

def do_yield()
yield
end

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

Which means you can assign to local variables in the closure's lexical scop=
e=20
from inside the closure.

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

David Vallner
 
D

Dave Cantrell

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
 
L

Lionel Thiry

Sam Kong a écrit :
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

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"
 
J

Jim Weirich

Sam said:
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
 
D

David Vallner

D=C5=88a Streda 08 Febru=C3=A1r 2006 05:57 Dave Cantrell nap=C3=ADsal:
Question: How is the above different from, say in pseudo code:

function do_something(in)
foo =3D in
end function

foo =3D "FOO"
do_something("BAR")

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

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

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

def do_yield
yield
end

foo =3D "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=20
# variable of the same name cannot be accessed here.
foo =3D "BAR"
puts foo # Outputs BAR.

do_yield {
foo =3D "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=
=20
the difference in behaviour when compared to global variables. Possibly, wi=
th=20
LISP-like dynamic scoping, a function could access a local variable from a=
=20
calling scope, but to my best knowledge, Ruby is a lexically scoped languag=
e.

David Vallner
 
D

David Vallner

I should also really make myself read long blocks of text to avoiding longe=
r=20
blocks of text.

D=C5=88a Streda 08 Febru=C3=A1r 2006 05:57 Dave Cantrell nap=C3=ADsal:
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=
=20
where you blooped up, foo isn't globally scoped. Not even in that code=20
snippet. Method definitions aren't a closure on their enclosing lexical sco=
pe=20
=2D they run in a separate scope.
class MethodList
def initialize
@methods =3D []
end

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

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

m =3D 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=
=20
feel like hacking up classes. If you can understand why i is four, then=20
indeed you've pretty much gotten the hang of all there is to it about how=20
blocks and closures work.

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

David Vallner
 
D

David Vallner

D=C5=88a =C5=A0tvrtok 09 Febru=C3=A1r 2006 02:14 David Vallner nap=C3=ADsal:
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=
=20
avoid writing longer blocks of text", in case anyone cares.
 
D

Dave Cantrell

David said:
I should also really make myself read long blocks of text to avoiding l= onger=20
blocks of text.
=20
D=C5=88a Streda 08 Febru=C3=A1r 2006 05:57 Dave Cantrell nap=C3=ADsal:
=20
There's a new term, subordinate functions. But this is essentially the = bit=20
where you blooped up, foo isn't globally scoped. Not even in that code=20
snippet. Method definitions aren't a closure on their enclosing lexical= scope=20
- 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=20
an assumption (I know, I know) that the function declared after the=20
variable foo was running in the same or subordinate scope as the=20
variable itself was declared --- whether global or not didn't seem to=20
matter, but was convenient to write.

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

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

-dave
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,997
Messages
2,570,240
Members
46,830
Latest member
HeleneMull

Latest Threads

Top