I thought this was the one that worked?

E

Eric Armstrong

#!/usr/bin/env ruby

repeat = 1

def run
number_of_times = repeat
number_of_times.downto(0) do
puts number_of_times
sleep 1
end
end

run
----------------------------

Fails: undefined variable, "repeat"
in first line of run method

I would have sworn that this was the
one that worked. I know that for a class
definition, the value of "repeat" isn't
known in an instance. That makes sense
to me (now).

But here, I'm in a script. I am continually
surprised by this behavior. In my mental
model, the run method is a "subsidiary"
context of the larger context, and as such
it should just "naturally" be aware of values
defined in the wider context.

Obviously, my mental model is way, way off,
which is why I keep getting surprised. Can
anyone enlighten me?

thanks
eric
 
L

Logan Capaldo

#!/usr/bin/env ruby

repeat = 1

def run
number_of_times = repeat
number_of_times.downto(0) do
puts number_of_times
sleep 1
end
end

run
----------------------------

Fails: undefined variable, "repeat"
in first line of run method

I would have sworn that this was the
one that worked. I know that for a class
definition, the value of "repeat" isn't
known in an instance. That makes sense
to me (now).

But here, I'm in a script. I am continually
surprised by this behavior. In my mental
model, the run method is a "subsidiary"
context of the larger context, and as such
it should just "naturally" be aware of values
defined in the wider context.

Obviously, my mental model is way, way off,
which is why I keep getting surprised. Can
anyone enlighten me?

thanks
eric

repeat is a local variable. class, def, and module introduce a new
scope without closing over their enclosing scope. Blocks introduce a
new scope that does enclose it's enclosing scope.

repeat = 1 # local variable repeat

def run # I just introduced a new scope. I can't see local variables
outside my body


define_method("run") { # I just introduced a new scope, but I'm also
a closure, I can see repeat.

Note that this issue has nothing to do with instances vs. classes, etc.
 
C

Chad Perrin

repeat is a local variable. class, def, and module introduce a new
scope without closing over their enclosing scope. Blocks introduce a
new scope that does enclose it's enclosing scope.

As I understand it, this means that class, def, and module use dynamic
local scope, and blocks use static/lexical scope. Is that accurate?
 
L

Logan Capaldo

As I understand it, this means that class, def, and module use dynamic
local scope, and blocks use static/lexical scope. Is that accurate?
All these examples are lexical scoping. Ruby doesn't really have
dynamic scoping although you can sort of abuse instance variables to
achieve similar effects.

The difference is that blocks are closures, where def, class, and
module aren't.


 
C

Chad Perrin

All these examples are lexical scoping. Ruby doesn't really have
dynamic scoping although you can sort of abuse instance variables to
achieve similar effects.

The difference is that blocks are closures, where def, class, and
module aren't.

Wait . . . you mean that *all blocks* are automagically lexical
closures, as though declared lexical variables within them have gone out
of scope? I imagine I'm probably misunderstanding you, but if not,
that's a pretty nifty bit of trickery.
 
L

Logan Capaldo

Wait . . . you mean that *all blocks* are automagically lexical
closures, as though declared lexical variables within them have
gone out
of scope? I imagine I'm probably misunderstanding you, but if not,
that's a pretty nifty bit of trickery.
I'm not sure I understand your question. All blocks (by blocks I mean
do / end and { } ) are (lexically scoped) closures.



--
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
Ben Franklin: "As we enjoy great Advantages from the Inventions of
others we should be glad of an Opportunity to serve others by any
Invention of ours, and this we should do freely and generously."
 
C

Chad Perrin

I'm not sure I understand your question. All blocks (by blocks I mean
do / end and { } ) are (lexically scoped) closures.

I'll use a Perl example:

sub foo {
my $bar = 1;
return sub { print ++$bar };
}

my $baz = foo();

Voila. $baz contains a lexical closure. This is the case because the
return value from foo() was lexically "closed" by virtue of $bar going
out of scope, but its value still being accessible via the coderef
returned from foo() and assigned to $baz.
 
J

Jacob Fugal

I'll use a Perl example:

sub foo {
my $bar = 1;
return sub { print ++$bar };
}

my $baz = foo();

Voila. $baz contains a lexical closure. This is the case because the
return value from foo() was lexically "closed" by virtue of $bar going
out of scope, but its value still being accessible via the coderef
returned from foo() and assigned to $baz.

How is this different from:

def foo
bar = 1
return proc{ bar += 1; puts bar }
end

begin
puts bar
rescue Exception => e
puts e
end
# => undefined local variable or method `bar' for main:Object

baz = foo
baz.call # => 2
baz.call # => 3
baz.call # => 4
...

Jacob Fugal
 
C

Chad Perrin

How is this different from:

def foo
bar = 1
return proc{ bar += 1; puts bar }
end

begin
puts bar
rescue Exception => e
puts e
end
# => undefined local variable or method `bar' for main:Object

baz = foo
baz.call # => 2
baz.call # => 3
baz.call # => 4

Who said it's different? I'm just asking about the foregoing statement
that all blocks are closures (which strikes me as improbable). That's
like saying that this will generate a closure (which it won't):

sub foo {
return sub { my $bar = 1; print ++$bar };
}
 
L

Logan Capaldo

I'll use a Perl example:

sub foo {
my $bar = 1;
return sub { print ++$bar };
}

my $baz = foo();

Voila. $baz contains a lexical closure. This is the case because the
return value from foo() was lexically "closed" by virtue of $bar going
out of scope, but its value still being accessible via the coderef
returned from foo() and assigned to $baz.

Yes. It is just like perl.

% cat closure.rb
def foo
bar = 1
lambda { puts (bar += 1) }
end

baz = foo()

baz.call
baz.call

% ruby closure.rb
-:13: warning: don't put space before argument parentheses
2
3


 
L

Logan Capaldo

Who said it's different? I'm just asking about the foregoing
statement
that all blocks are closures (which strikes me as improbable). That's
like saying that this will generate a closure (which it won't):

sub foo {
return sub { my $bar = 1; print ++$bar };
}

In ruby it does. I'm having difficulty thinking of a simple example
to demonstrate it, but basically it grabs the whole environment
whether theres a reference to a variable or not. It does this to
allow things like:

def foo
x = 1
lambda { puts eval("X".downcase) }
end

You might say, so it just notices that you're using eval. But
consider this:

def bar
x = 1
lambda { puts send("lave".reverse, "y".sub(/y/, 'x')) }
end

Obviously contrived examples, but this is part of the reason blocks
"leak" memory.


 
J

Just Another Victim of the Ambient Morality

Chad Perrin said:
Who said it's different? I'm just asking about the foregoing statement
that all blocks are closures (which strikes me as improbable). That's
like saying that this will generate a closure (which it won't):

sub foo {
return sub { my $bar = 1; print ++$bar };
}

Okay, the mere fact that you "used a Perl example" demonstrates that
there is a miscommunication going on here. Why would an example from Perl
clear anything up? It's a different language with different semantics. Are
you hoping that we all know Perl?

I suspect what's happening here is that we're mixing up code blocks with
"Ruby blocks."
These are examples of what most programmers would call blocks but are
_not_ Ruby blocks:


def some_method_as_function
# code block goes here but
# in the Ruby world, this is not a "block..."
end

while some_condition
# code block goes here but
# in the Ruby world, this is not a "block..."
end


These are Ruby blocks:


[1, 2, 3].each do |i|
# this is a Ruby block and
# this is a closure
end

some_local_variable = 2
func = lambda { puts some_local_variable } # a one line block and closure


All things that are called "blocks" in Ruby are closures.
Do you still have any questions on the matter?
 
C

Chad Perrin

In ruby it does. I'm having difficulty thinking of a simple example
to demonstrate it, but basically it grabs the whole environment
whether theres a reference to a variable or not. It does this to
allow things like:

def foo
x = 1
lambda { puts eval("X".downcase) }
end

You might say, so it just notices that you're using eval. But
consider this:

def bar
x = 1
lambda { puts send("lave".reverse, "y".sub(/y/, 'x')) }
end

Obviously contrived examples, but this is part of the reason blocks
"leak" memory.

Um, no . . . that's not the equivalent of what I just posted. To make
it equivalent, you'd have to do something like this:

def foo
lambda { x = 1; puts eval("X".downcase) }
end
 
C

Chad Perrin

Okay, the mere fact that you "used a Perl example" demonstrates that
there is a miscommunication going on here. Why would an example from Perl
clear anything up? It's a different language with different semantics. Are
you hoping that we all know Perl?

I'm hoping that demonstrating how it does or does not work in Perl will
make it clear what I'm talking about, based on similar syntax between
the languages, so that I can find out what does and does not work in
Ruby. Duh?

I suspect what's happening here is that we're mixing up code blocks with
"Ruby blocks."
These are examples of what most programmers would call blocks but are
_not_ Ruby blocks:

I know there's a difference, thanks. The problem is that nobody seems
to be addressing the fact that for a proper closure, as I understand it,
the closure variable has to go out of scope while the block of code does
not. If Ruby blocks do something automagical to make variables declared
within them go out of scope while the block is still in scope, I'd like
to know. If there's some other way to "close" the block's data while
the block is still available, I'd like to know that, too.

def some_method_as_function
# code block goes here but
# in the Ruby world, this is not a "block..."
end

while some_condition
# code block goes here but
# in the Ruby world, this is not a "block..."
end


These are Ruby blocks:


[1, 2, 3].each do |i|
# this is a Ruby block and
# this is a closure
end

So explain how it is a closure. Please.

some_local_variable = 2
func = lambda { puts some_local_variable } # a one line block and closure

Am I to assume that some_local_variable is going out of scope outside of
the lambda declaration at some point, to create that closure?

All things that are called "blocks" in Ruby are closures.
Do you still have any questions on the matter?

Yes. Are you just failing to explain how these things are closures, or
do you not know what a closure is? You keep telling me blocks are
closures somehow, but you're not explaining how. Saying it doesn't make
it so. Please explain what I'm missing.
 
C

Chad Perrin

Yes. It is just like perl.

% cat closure.rb
def foo
bar = 1
lambda { puts (bar += 1) }
end

baz = foo()

baz.call
baz.call

% ruby closure.rb
-:13: warning: don't put space before argument parentheses
2
3

Okay. Looks like a closure. It looks like a closure because of the
relationship of bar to the return-value block of code. I've been told
that all blocks are closures, though -- and I don't see how it's still a
closure if the "bar = 1" is removed from foo.
 
H

Hal Fulton

Chad said:
Am I to assume that some_local_variable is going out of scope outside of
the lambda declaration at some point, to create that closure?





Yes. Are you just failing to explain how these things are closures, or
do you not know what a closure is? You keep telling me blocks are
closures somehow, but you're not explaining how. Saying it doesn't make
it so. Please explain what I'm missing.

I'm unfamiliar with your assertion that the context has to go
out of scope before a closure exists.

Not trying to argue, just stating my perception.

Maybe you can point us to a formal definition somewhere.

I would have said that a closure *will* retain its variables
*if/when* they go out of scope.

Bad analogy: We might loosely define a car as "a machine that
goes places," but it's still a car even when it sits still.

My impression is that you're applying the definition too
rigidly.

Again, I'm not certain I'm right. No flames, no insults.


Hal
 
L

Logan Capaldo

Um, no . . . that's not the equivalent of what I just posted. To make
it equivalent, you'd have to do something like this:

def foo
lambda { x = 1; puts eval("X".downcase) }
end


It's still a closure.

Like I said it's hard to give you an example, but ruby creates a
closure for a block regardless. That lambda still has a reference to
the enclosing scope of foo, even if their are no variables there. Ok
I came up with a good one:

% cat closure2.rb
class A
def initialize
@a = 1
end

def foo
lambda { puts @a }
end

def set_var(val)
@a = val
end
end

a = A.new
baz = a.foo
baz.call

a.set_var( "Hello" )

baz.call

% ruby closure2.rb
1
Hello


In this case it happens to be closing over "self", but since this is
ruby there is _always_ a self, all blocks are closures. (and you
always have the associated overhead)

 
L

Logan Capaldo

Okay. Looks like a closure. It looks like a closure because of the
relationship of bar to the return-value block of code. I've been told
that all blocks are closures, though -- and I don't see how it's
still a
closure if the "bar = 1" is removed from foo.

Oh yeah, here's another example:

def foo
return lambda { puts eval("x") }, binding
end

closure, bnding = foo()
eval("x = 7", bnding)

closure.call


Basically, it's impossible not to create a closure with a block in
ruby, there is always an implicit closure.
 
D

dblack

Hi -

Okay. Looks like a closure. It looks like a closure because of the
relationship of bar to the return-value block of code. I've been told
that all blocks are closures, though -- and I don't see how it's still a
closure if the "bar = 1" is removed from foo.

It's a closure because it carries the context of its creation with it.
It doesn't matter whether that context has zero, one, or fifty local
variables; the same thing still happens.

Here's some sort of negative evidence:

def foo
lambda { puts bar }
end

bar = 1
foo.call # error: bar is undefined

Here, the statement "puts bar" is executed in the context of the
creation of the block (rather than the context of its execution) --
and since there's no bar there, an exception is raised.

Some subtle but possible interesting hair-splitting:

Of course, not all blocks end up getting turned into Proc objects;
some remain just syntactic constructs:

a = 1
[1,2,3].each {|x| puts x + a }

I guess you could debate whether that block is a closure, since it
never leaves the context where it's created -- so there's nothing
really remarkable about the fact that a is still visible inside it.
And every time you put its closureness to the test, so to speak,
you've turned it into a Proc, so technically the Proc, rather than the
block, is the closure.

(Like I said, subtle hair-splitting :)


David

--
http://www.rubypowerandlight.com => Ruby/Rails training & consultancy
----> SEE SPECIAL DEAL FOR RUBY/RAILS USERS GROUPS! <-----
http://dablog.rubypal.com => D[avid ]A[. ]B[lack's][ Web]log
http://www.manning.com/black => book, Ruby for Rails
http://www.rubycentral.org => Ruby Central, Inc.
 
C

Chad Perrin

I'm unfamiliar with your assertion that the context has to go
out of scope before a closure exists.

Not trying to argue, just stating my perception.

Maybe you can point us to a formal definition somewhere.

How's this?:

Closure
A "closure" is an expression (typically a function) that can have
free variables together with an environment that binds those
variables (that "closes" the expression).

I found it on this page:

http://jibbering.com/faq/faq_notes/closures.html

I also found this:

http://www.perl.com/pub/a/2002/05/29/closure.html

I would have said that a closure *will* retain its variables
*if/when* they go out of scope.

Bad analogy: We might loosely define a car as "a machine that
goes places," but it's still a car even when it sits still.

My understanding is that the vehicle is a car, and the use of it is a
drive (to the country, or whatever). Similarly, a block of code that
can be passed around like a first-class member is a block, or lambda, or
whatever you want to call it today: if you pass it to a function as an
argument, it's a callback, and if you lexically close it, it's a
closure.

My impression is that you're applying the definition too
rigidly.

I suppose that's possible, but I really don't think so. Do you have a
formal definition of a closure that specifically contradicts my
understanding?
 

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

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top