Forum: Ruby What's the difference between copying and sharing in closure

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Sam K. (Guest)
on 2006-02-07 19:41
(Received via mailing list)
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 V. (Guest)
on 2006-02-07 20:19
(Received via mailing list)
DÅ?a Utorok 07 Február 2006 18:38 Sam K. napísal:
> real closure. It's not just a copy.
> 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 V.
Lionel T. (Guest)
on 2006-02-08 18:34
(Received via mailing list)
Sam K. a écrit :
> real closure. It's not just a copy.
> 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"


--
Lionel T.

Personal web site: http://users.skynet.be/lthiry/
Jim W. (Guest)
on 2006-02-08 19:06
Sam K. 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 W.
Dave C. (Guest)
on 2006-05-03 20:56
(Received via mailing list)
> 	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 N. (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
David V. (Guest)
on 2006-08-03 15:44
(Received via mailing list)
DÅ?a Streda 08 Február 2006 05:57 Dave C. napísal:
> > 	puts foo # Outputs "BAR".
>    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 V.
David V. (Guest)
on 2006-08-03 15:44
(Received via mailing list)
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 C. 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.

>        i = param
>    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 V.
David V. (Guest)
on 2006-08-03 16:22
(Received via mailing list)
DÅ?a Å tvrtok 09 Február 2006 02:14 David V. 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 C. (Guest)
on 2006-08-03 16:22
(Received via mailing list)
David V. wrote:
> 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
This topic is locked and can not be replied to.