I thought this was the one that worked?

L

Logan Capaldo

Okay, let's translate that question into the terms we're actually
discussing.

1. If a tree falls in the forest, and there's no one around to hear
it, does it still make a sound?

2. If a lambda has the ability to access its context, but there
isn't
any context to access, is it still a closure?



Not yet. See above, re: blocks falling in a forest.

I'm beginning to think the question of whether it's actually a closure
really IS a question for a programming koan, after all. If so, I'm
glad
we've at least narrowed the discussion down to this point at last.

It'd be pretty neat to be responsible for the creation of a new
programming koan, anyway. I'm going to go add this to my sig rotation
right now.


More examples to chew on:

class A
def make_closurish_thing
lambda { puts "Hello" }
end
end


a = A.new
c = a.make_closurish_thing
c.call
def a.puts(*args)
super("Access of enclosing scope")
super
end
c.call


class B
def close_me
x = 1
lambda{|x| eval(x) }
end
end

b = B.new
c = b.close_me

c.call("puts 'Am I a closure?'")
c.call("puts 'How about now?'; puts x")
 
J

Just Another Victim of the Ambient Morality

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.

I would debate that it _is_ a closure because, although it's defined
where its context is, that's irrelevant. What matters is that the block
is
passed into the "each" method of the Array class and is executed there,
where "a" is not visible. Thus, the block was executed outside of the
scope
of "a" while still having access to it. How is this possible? It's
possible because the block that was passed in is a closure...
QED.

It's not exactly passed to the method, though. You can capture it in
the method -- in which case, it becomes a Proc object, and then
there's no issue (hair-splitting or otherwise) about its being a
closure.

If you don't capture it, you can yield to it -- but then you're
yielding to the block, not calling a Proc object derived from the
block.

...and this would be a problem if the definition of a closure is the
calling of a Proc object derived from a block. Alas, that is _not_ the
definition of a closure...
When we yield to the block, we are doing so in our method, which is
_not_ the same scope as the scope where the block was created. Yet, the
block we're yielding to still has access to that other scope. How? Because
it is a closure...

It comes down to the fact that blocks are syntactic constructs, while
Procs are first-class objects.

I'm not sure why this has anything to do with anything...
 
D

dblack

Hi --

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.

I would debate that it _is_ a closure because, although it's defined
where its context is, that's irrelevant. What matters is that the block
is
passed into the "each" method of the Array class and is executed there,
where "a" is not visible. Thus, the block was executed outside of the
scope
of "a" while still having access to it. How is this possible? It's
possible because the block that was passed in is a closure...
QED.

It's not exactly passed to the method, though. You can capture it in
the method -- in which case, it becomes a Proc object, and then
there's no issue (hair-splitting or otherwise) about its being a
closure.

If you don't capture it, you can yield to it -- but then you're
yielding to the block, not calling a Proc object derived from the
block.

...and this would be a problem if the definition of a closure is the
calling of a Proc object derived from a block. Alas, that is _not_ the
definition of a closure...
When we yield to the block, we are doing so in our method, which is
_not_ the same scope as the scope where the block was created. Yet, the
block we're yielding to still has access to that other scope. How? Because
it is a closure...

You could say, though, that when you do this:

def x
a = 1
puts a
end

def y
x
end

you're calling x from your method y, and x has access to local
variables not defined in y, so x must be a closure.
I'm not sure why this has anything to do with anything...

Consider an if statement:

y = 1
if x
y
end

The code in the middle is flat, as regards scope. Furthermore, you
can't do anything with it; you can't send that chunk of code
somewhere, open it up, and find y. It's just an expression or
statement among other expressions or statements.

A code block has one foot in that camp. When you see:

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

you're seeing, in a sense, a flat scope -- that is, the variable a
just gets used, as it might if it were in an if statement.

That's only part of the story, though. The other parts are, first,
that variables created inside the block are not in scope when the
block exits (so its scope is definitely not flat); and, second, the
fact that it's so easy to convert a block to a Proc that blocks feel
like first-class objects, even though they aren't.


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

Just Another Victim of the Ambient Morality

Chad Perrin said:
Okay, let's translate that question into the terms we're actually
discussing.

1. If a tree falls in the forest, and there's no one around to hear
it, does it still make a sound?

2. If a lambda has the ability to access its context, but there isn't
any context to access, is it still a closure?

How about this:

3. If a lambda has the ability to access its context and nothing's around to
use it, does it still make a closure?

I think 3 is a better characterization than 2. Here's some code to
demonstrate:


variable = 2 # this is the context

[1, 2, 3].each do |i|
# it sounds as if you think this is the "context"
puts i + variable

# either way, we're using the context so
# we agree that this is a closure
end

[4, 5, 6].each do |i|
# a context exists here since we
# have access to "variable"
puts i

# but we don't use it.
# are we still a closure?
end

I'm beginning to think the question of whether it's actually a closure
really IS a question for a programming koan, after all. If so, I'm glad
we've at least narrowed the discussion down to this point at last.

I agree that we have finally narrowed down the discussion. This might
have happened sooner if I had actually followed the thread...

It'd be pretty neat to be responsible for the creation of a new
programming koan, anyway. I'm going to go add this to my sig rotation
right now.

Ah, it's over-rated...
Is a loop a loop if it only loops once?
Does a parameter exist if we don't actually access it?

Not to be insulting but there's a part of me that thinks that you came
up with this distinction to hide the (embarrassing?) fact that you didn't
know that all blocks were closures...
 
J

Just Another Victim of the Ambient Morality

You could say, though, that when you do this:

def x
a = 1
puts a
end

def y
x
end

you're calling x from your method y, and x has access to local
variables not defined in y, so x must be a closure.

Now you're narrowly construing my statement to a ludicrous degree.
There is context to what I said, you know?
In your example, method "x" has access to variable "a" because that
variable is in its own context, not its _enclosing_ context. This is a
better example:

a = 1
def x
puts a
end

def y
x
end


Then x _will_ be a closure but, then again, we already knew this...

I'm not sure why this has anything to do with anything...

Consider an if statement:

y = 1
if x
y
end

The code in the middle is flat, as regards scope. Furthermore, you
can't do anything with it; you can't send that chunk of code
somewhere, open it up, and find y. It's just an expression or
statement among other expressions or statements.

A code block has one foot in that camp. When you see:

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

you're seeing, in a sense, a flat scope -- that is, the variable a
just gets used, as it might if it were in an if statement.

That's the whole point of closures. It looks like variable "a" "just
gets used," even though the process of it being used is quite complex.
If I understand what you mean by "flat," then it isn't flat. It only
_looks_ flat.
Is this an example of "flat" scope?


a = "foo"
b = a + "bar"
puts b


It may look "flat" but scopes have come and gone.
Your example took a block and sent it to the method "each," which has
it's own scope, where the block would have ordinarily had no hope of
accessing the variable "a" except that it was a closure...

That's only part of the story, though. The other parts are, first,
that variables created inside the block are not in scope when the
block exits (so its scope is definitely not flat); and, second, the
fact that it's so easy to convert a block to a Proc that blocks feel
like first-class objects, even though they aren't.

I'm not certain that the other parts of "the story" are relevant.

Incidentally, having variables created inside the block be in scope when
the block exits is planned for Ruby2. Indeed, if memory serves me, it is
Matz's "most regretting" design decision that it doesn't already do this...
So, I guess it will then be "flat."
Personally, I look forward to this feature...
 
D

dblack

Hi --

Now you're narrowly construing my statement to a ludicrous degree.

I'm just discussing the topic at hand, I think. It's really not about
this or that statement; I just like to examine things like this.
There is context to what I said, you know?
In your example, method "x" has access to variable "a" because that
variable is in its own context, not its _enclosing_ context. This is a
better example:

a = 1
def x
puts a
end

def y
x
end


Then x _will_ be a closure but, then again, we already knew this...

But the a inside x and the a outside x are unrelated. Method
definition blocks (with def) aren't closures.
It comes down to the fact that blocks are syntactic constructs, while
Procs are first-class objects.

I'm not sure why this has anything to do with anything...

Consider an if statement:

y = 1
if x
y
end

The code in the middle is flat, as regards scope. Furthermore, you
can't do anything with it; you can't send that chunk of code
somewhere, open it up, and find y. It's just an expression or
statement among other expressions or statements.

A code block has one foot in that camp. When you see:

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

you're seeing, in a sense, a flat scope -- that is, the variable a
just gets used, as it might if it were in an if statement.

That's the whole point of closures. It looks like variable "a" "just
gets used," even though the process of it being used is quite complex.
If I understand what you mean by "flat," then it isn't flat. It only
_looks_ flat.

Right -- see the "other parts of the story", below.
Is this an example of "flat" scope?
Yes.

a = "foo"
b = a + "bar"
puts b


It may look "flat" but scopes have come and gone.

I only see one local scope there. Where do you see others?
Your example took a block and sent it to the method "each," which has
it's own scope, where the block would have ordinarily had no hope of
accessing the variable "a" except that it was a closure...

The part I'm not convinced of is "sent". It provided a code block,
but it didn't send it.
I'm not certain that the other parts of "the story" are relevant.

See above; it's just different ways of looking at a block, or ways of
looking at different characteristics of a block.
Incidentally, having variables created inside the block be in scope when
the block exits is planned for Ruby2. Indeed, if memory serves me, it is
Matz's "most regretting" design decision that it doesn't already do this...
So, I guess it will then be "flat."
Personally, I look forward to this feature...

I'm among the few that like it the way it is, but I think we're on the
losing end of this decision-making process for 2.0 :)


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

Just Another Victim of the Ambient Morality

I only see one local scope there. Where do you see others?

The scope in the method "+" came and went. The scope in the method
"puts" came and went as well...

The part I'm not convinced of is "sent". It provided a code block,
but it didn't send it.

It got "sent" as much as anything gets sent around here (in Ruby)...
I already posted this somewhere else on this thread but I guess I'll
post it, again, here. The "each" method in class "Array" might have been
implemented something like this:


class Array
def each
i = 0
while i < self.size
# this is where the block is executed
# far away from the scope where "a" is defined...
# the block was "sent" here...
yield self

i += 1
end
end
end

# this is the sample code you provided...

a = 10
# the block is defined here but "sent" to the "each" method...
[1, 2, 3].each {|x| puts x * a }


Of course, I doubt anyone would actually implement "Array#each" like
that. They might do something like this:


class Range
def each
i = first
while i <= last
yield i

i += 1
end
end
end

class Array
def each
(0..(size - 1)).each { |i| yield self }
end
end

# again, this is the sample code you provided...

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

I'm among the few that like it the way it is, but I think we're on the
losing end of this decision-making process for 2.0 :)

There was a decision-making process?
 
G

Gennady Bystritsky

Chad,

Sorry for jumping in late in the game, however I think this can explain
why a block is a closure even when it does not seemingling reference any
variables from the scope it was created in:

class A
def f
proc {
puts "abc"
}
end
end

a =3D A.new

b =3D a.f
b.call

class << a
def puts(*args)
$stderr.puts args.map { |_a| _a.upcase }
end
end

b.call

Procuces:

abc
ABC

Closure references not only variables, but anything else in the scope,
like methods, etc. And the redefined method may reference variables in
that scope, that's why the "full blown" closures are created no matter
what the block [currently] references.

Gennady.
-----Original Message-----
From: Chad Perrin [mailto:p[email protected]]=20
Sent: Tuesday, August 01, 2006 12:45 AM
To: ruby-talk ML
Subject: Re: I thought this was the one that worked?
=20
=20
I'm talking about what happens inside the ruby interpreter when it =20
sees that code. As far as ruby is concerned, it doesn't=20 know how to =20
make a block that isn't a "closure". The same code path=20 gets followed =20
whether ruby sees
=20
def foo
a =3D 1
lambda { ... a ... }
end
=20
or
=20
def foo
lambda { ... }
end
=20
All the "closure bookkeeping" is done whenever you create a=20 block. No =20
extra bookkeeping is done whether or not you take advantage of the =20
fact that you have a "closure".
=20
Arrrrrrgh. We're talking past one another. I can see where at least
some of the disconnect is occurring, but I don't seem to be=20
able to get
across to you what you're not saying so that you'll explain it.
=20
I give up for now. I'll just see if I can learn what's actually going
on with blocks and closures in Ruby from some other source.
=20
--=20
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
"There comes a time in the history of any project when it=20
becomes necessary
to shoot the engineers and begin production." - MacUser, November 1990
=20
=20
 
G

Gennady Bystritsky

Yes, it is:

class B
def initialize(a)
@a =3D a
end

def each
yield # First block invocation

@a.z =3D 77 # Changing the scope in=20
class << @a # which the block was created
def puts(*args)
$stderr.puts @z, *args.map { |_a| _a.upcase }
end
end

yield # Second block invocation
end
end

class A
attr_accessor :z
def f
array =3D B.new(self)

### HERE'S YOUR FRAGMENT
array.each do ||
puts "Hello world!"
end
### YOUR FRAGMENT ENDS HERE
end
end

A.new.f

Procuces:

Hello world!
77
HELLO WORLD!=20

Ruby does not assume what a method is going to do with a block, it just
creates a closure.

Gennady.
-----Original Message-----
From: Chad Perrin [mailto:p[email protected]]=20
Sent: Tuesday, August 01, 2006 3:32 AM
To: ruby-talk ML
Subject: Re: I thought this was the one that worked?
=20
=20
Well, I think that meshes just fine with my take on it...
To tell you the truth, I didn't expect you to ask me what my=20
understanding of a closure is because I thought I had=20 already demonstrated=20
it with the code sample I gave. Of course, you're busy=20 digesting that and I=20
appreciate it. It means you're actually trying to=20 understand me (rather=20
than just arguing at me. Something that happens all too often...).
If I may be so bold, I think that, perhaps, blocks=20 don't look like=20
closures to you because you don't realize that methods that=20 take blocks,=20
like "each," "inject," "collect," etc.. are methods (member=20 functions, if=20
you're a C++ guy) and not arbitrary language constructs,=20 like "if," "while,"=20
"for," etc...
=20
I'm not exactly a C++ guy. I suppose I'm more of a Perl guy than
anything else, judging by my familiarity with languages and the
assumption of a lack of loathing (I'm as familiar with PHP as=20
with Perl,
but I don't like it very much, for instance). I am familiar with many
of the idioms and much of the semantic and syntactic theory=20
of a lot of
languages, even if I don't use them much, though. The=20
dangers of doing
consulting and industry analysis for money. . . .
=20
=20
=20
Now that I think about it, this whole argument can be=20 settled with one=20
simple question. If you doubt that all blocks are closures=20 then show us a=20
counter-example. Show us a "block" that is not a closure=20 and I will show=20
you how it's not a block...
=20
Is this a closure? You tell me (and tell me how, if it is):
=20
array.each do ||
puts "Hello world!"
end
=20
--=20
CCD CopyWrite Chad Perrin [ http://ccd.apotheon.org ]
print substr("Just another Perl hacker", 0, -2);
=20
=20
 
J

Jacob Fugal

It sounds like what you're saying is that the lexical scope of the code
block (proc/lambda/blah) is what makes it a closure, and not the
connection with, and OOPish protection/encapsulation of, something that
started outside the code block and went out of scope externally to the
code block.

No, what he's saying is that what makes it a closure is exactly that
form of encapsulation (capture, I would say) of its surrounding
*environment*. I think that's the key distinction here.

You seem set in the belief that a closure is around a specific
variable or group of variables. If those variables don't exist, then
the closure must not exist. Unfortunately the current definition on
Wikipedia (which I consider useful, but not authoritative) reflects
that type of thinking.

But the definition I learned in the University, have seen in multiple
CS theory texts and to which David is referring here is that the
closure is around an *environment*, regardless of what variables from
that environment may or may not exist and/or be used. So even if the
environment were empty, or the lambda/proc didn't reference any
variables from the environment, the lambda/proc is still capturing
that environment, forming a closure around it.

Even then, an empty environment can't exist in Ruby. There's always at
least one "variable" in the environment that will be accessible in the
closure: self. Take the following example:

class Foo
def bar
lambda{ baz }
end
end

foo = Foo.new
bar = foo.bar

class << foo
def baz
puts "quux"
end
end

bar.call
# prints "quux"

It can't be argued that the definition of baz is being captured from
its surrounding environment; it didn't exist when the lambda was
created, and even when it was defined, it was in a different
environment. What the lambda formed a closure around was the
environment. Included in that environment was self. When the
interpreter sees "baz", it sends (essentially, there's of course
permission checks and such) "baz" to the self defined in the lambda's
environment; that is, the self captured by the closure. The method
lookup chain on that object can then find the definition of baz we put
into the singleton class for foo.

So the environment is never empty. The only remaining argument would
be something like "If a lambda/proc doesn't use any values from it's
environment (even self, so no receiverless method calls), then it's
not a closure." I say it would be a closure anyways, since the
captured environment is still there, still taking up memory, still
accessible, only unused.
[snip]
it would mean that absolutely any unnamed subroutine passed by reference in
Perl is a closure. In fact, by the implied definition of a closure that
produces, this is a closure in Perl:

sub bar {
sub { print "Hello world!\n" };
}

$foo = bar();

$foo->();

From what I learned in my CS courses and my knowledge of how perls
scopes work, that *is* a closure. Simply because the facilities
provided by a construct aren't used doesn't make the construct cease
to be...
That's the equivalent of this, in Ruby:

def bar
lambda { puts "Hello world!" }
end

foo = bar

foo.call

Again, also a closure. But this one even more specifically: in the
method call to puts, you're using the value of self captured in the
environment closed over by the lambda.

Jacob Fugal
 
C

Chad Perrin

Yes, it is:

Great. You just failed to answer the rest of my question, though.
Instead, you built a bunch of code around my piece that has nothing to
do with my question. It was less than strictly helpful.

Also, I would appreciate it if you'd not top post in reply to me --
especially not in TOFU style.
 
C

Chad Perrin

How about this:

3. If a lambda has the ability to access its context and nothing's around to
use it, does it still make a closure?

I think 3 is a better characterization than 2. Here's some code to
demonstrate:

I don't. A closure is a closure whether you use it or not, just as a
remote control for the TV is a remote control for the TV whether you use
it or not. What's actually relevant to the discussion is more like the
question of whether a remote control for the TV is still a remote
control for the TV if it was built with no TV on which it could operate.

variable = 2 # this is the context

[1, 2, 3].each do |i|
# it sounds as if you think this is the "context"
puts i + variable

# either way, we're using the context so
# we agree that this is a closure
end

Actually . . . no. If that was a closure just because there was a
variable nearby that it could access, then in any script that contains a
lexical variable at the "base level" of the script, and in any script
that has global variables, every scope enclosed within the scope of the
script would be a closure.

I agree that we have finally narrowed down the discussion. This might
have happened sooner if I had actually followed the thread...

Unfortunately, it was David and possibly one or two others who have been
instrumental in narrowing it down. You're still talking about things
that don't seem to bear on what I was saying at all.

Not to be insulting but there's a part of me that thinks that you came
up with this distinction to hide the (embarrassing?) fact that you didn't
know that all blocks were closures...

Not to be insulting, but I don't think you've grasped the fact that
there's more to being a closure than being a language construct that
someone once said was a closure.
 
C

Chad Perrin

No; what makes something a closure, as I understand it, is that it
carries the context of its creation with it. Also, there's actually a
difference between a block on the one hand, and a Proc or lambda on
the other. (See my last couple of posts.)

That's far too broad to even be a meaningful distinction. It's
tantamount to claiming that in a program where everything has lexical
scope, everything is a closure -- because everything's "context"
(essentially its scope) is always applicable to it as it was at the
point of its creation with lexical scope.

I think of a closure as a kind of suitcase: you pack it in one place,
and unpack it somewhere else. Even if it's empty, though, it's still
a suitcase.

That makes more sense than what you said above (that I quoted above and
to which I responded above): it sounds like we're back to the koan here.
Specifically, it sounds like what you're saying here would indicate that
this is a closure:

def foo
bar = 0
lambda { puts bar += 1 }
end

baz = foo

and this is a closure:

def foo
lambda { puts "Hellow world!" }
end

bar = foo

because it does the same things with scope, but this is not a closure:

foo = 0

foo.each do
puts bar += 1
end

because its functionally equivalent to accessing global scope (which
even GWBASIC can do). Unfortunately, I suspect that you would say that
last example actually is a closure, and your "think of a closure"
description above was simply not precise enough.

I'd accept the description you gave above as being koan-compliant, with
the first two being closures within a broad but theoretically applicable
definition of a closure, as long as the apparent meaning of your
definition-by-analogy that excludes the third example is the definition
you're using. Once you interpret it to include the third definition, I
have a distinct problem with it.

Another way to look at it is this: if you decide that the lambda in
your example above is not a closure, then you have to come up with
separate explanations for everything it does that is closure-like.
If you look at it as a closure, however, there's nothing unaccounted
for.

. . and that's why it makes sense to think of it as fitting a
koan-compliant definition of a closure as you explained above, so long
as what it carries around with it is actually *a lexically closed
scope*, even if there's nothing meaningful within that closed scope.

That still doesn't make all blocks closures, though.
 
C

Chad Perrin

I don't. A closure is a closure whether you use it or not, just as a
remote control for the TV is a remote control for the TV whether you use
it or not. What's actually relevant to the discussion is more like the
question of whether a remote control for the TV is still a remote
control for the TV if it was built with no TV on which it could operate.

Okay, that wasn't quite right, either. It's more like this:

Is a remote control for the TV still a remote control for the TV if the
TV cannot turn on or off, has only one volume setting, and has only one
channel, and if the remote has no buttons for sending any signals to the
TV, but the remote still has a transmitter and the TV still has a
receiver?
 
C

Chad Perrin

No, what he's saying is that what makes it a closure is exactly that
form of encapsulation (capture, I would say) of its surrounding
*environment*. I think that's the key distinction here.

You seem set in the belief that a closure is around a specific
variable or group of variables. If those variables don't exist, then
the closure must not exist. Unfortunately the current definition on
Wikipedia (which I consider useful, but not authoritative) reflects
that type of thinking.

The current definition on Wikipedia is scattered, vague, and without
salient points on this matter. I've read through the thing from end to
end, and the article spends an awful lot of time failing to concretely
define a closure.

But the definition I learned in the University, have seen in multiple
CS theory texts and to which David is referring here is that the
closure is around an *environment*, regardless of what variables from
that environment may or may not exist and/or be used. So even if the
environment were empty, or the lambda/proc didn't reference any
variables from the environment, the lambda/proc is still capturing
that environment, forming a closure around it.

That's a definition of a closure that I would at this point at least not
call "wrong", even if it doesn't strike me as particularly "right"
either, because the explanations of closures I've seen can potentially
be interpreted in that manner. Even so, I'd say that to fit a strict
definition of a closure, the chunk of code in question needs to exit its
enclosing scope before it's a closure -- else it's not "closed".

Even then, an empty environment can't exist in Ruby. There's always at
least one "variable" in the environment that will be accessible in the
closure: self. Take the following example:

class Foo
def bar
lambda{ baz }
end
end

foo = Foo.new
bar = foo.bar

class << foo
def baz
puts "quux"
end
end

bar.call
# prints "quux"

Now you're arguing that there does, indeed, have to be a variable.
What? In any case, this can happen in Ruby:

class Foo
def bar
lambda { puts "quux" }
end
end

foo = Foo.new
bar = foo.bar

bar.call

. . so I don't see your point. Cut out the middleman, and it still
works.
 
C

Chad Perrin

...and this would be a problem if the definition of a closure is the
calling of a Proc object derived from a block. Alas, that is _not_ the
definition of a closure...
When we yield to the block, we are doing so in our method, which is
_not_ the same scope as the scope where the block was created. Yet, the
block we're yielding to still has access to that other scope. How? Because
it is a closure...

We're back to the absurdly broad here:

If any "thing" that has access to another scope is a closure, that makes
every nested "thing" in a language with global and/or lexical scope a
"closure". That's a little like defining a "human" as "has four limbs".
 
C

Chad Perrin

a = 1
def x
puts a
end

def y
x
end


Then x _will_ be a closure but, then again, we already knew this...

If that's the case, then *everything else* is a closure too, because
everything else has access to a -- at least where lexical or global
scope exists.
 
J

Jacob Fugal

Specifically, it sounds like what you're saying here would indicate that
this is a closure:

def foo
bar = 0
lambda { puts bar += 1 }
end

baz = foo

and this is a closure:

def foo
lambda { puts "Hellow world!" }
end

bar = foo

Yes, this is exactly the point we've been trying to get across. Both
of these are closures.
but this is not a closure:

foo = 0

foo.each do
puts bar += 1
end

This is where we get into what David Black described as hair
splitting. Lets move away from each, and to a set of more general
methods:

greeting = "Hello"

# 1) explicit proc
def foo(callback)
callback.call("world")
end

callback = lambda{ |arg| puts "#{greeting}, #{arg}!" }
foo(callback)
# prints "Hello, world!"

# 2) block converted to proc
def bar(&callback)
callback.call("world")
end

bar{ |arg| puts "#{greeting}, #{arg}!" }
# prints "Hello, world!"

# 3) block yielded to
def baz
yield "world"
end

baz{ |arg| puts "#{greeting}, #{arg}!" }
# prints "Hello, world!"

The first example obviously uses a closure. While true that the
environment where "greeting" is defined never goes out of scope, it is
not visible within the context where callback is actually executed.

The second example performs the exact same operation as the first, the
only difference is the syntax and *when* the chunk of code becomes a
proc.

The third example simply removes the explicit conversion from a block
to a proc, using yield instead. As such, the semantics between all
three are similar and example three can be arguably said to use a
closure as well. This is the stance Gennady and "the Victim" are
taking.

David's point, however, is that in the third example, when yield is
used instead of converting it to a block, the code inside the block
never really leaves the context in which it is created, because of the
way non-converted blocks are implemented. More specifically, that
implementation doesn't need to store that environment. This is a
probable reason for the speed difference between converted blocks
(first class procs) and unconverted blocks (yield). Since the
environment is never stored, it's not really a closure *in the
implementation*. This is the hair that David was splitting.

But semantically, whether a block is converted to a proc or just
yielded to, the behavior regarding variable scope is identical; so if
one is a closure, it's useful to refer to the other as a closure as
well, even if it's not implemented as a true closure. In this sense,
all blocks are closures.

Jacob Fugal
 
C

Chad Perrin

But the a inside x and the a outside x are unrelated. Method
definition blocks (with def) aren't closures.

Wait . . . are you serious? Ruby is using dynamic scope here? Ugh.
 

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,774
Messages
2,569,598
Members
45,144
Latest member
KetoBaseReviews
Top