Lexical scope and closures

I read recently a flaw I did not previously know about…

"Blocks (closures) in Ruby do not introduce a new scope, only methods
do.
This is generally acknowledged as a flaw by the Ruby community but is
left
in for backwards compatibility. For example:

x = 0
[1,2,3].each{|x| print x}
#x now equals 3"

Of course this is just wrong. I am hoping no one expects an
implementation of Ruby to actually behave this way.

Is it generally accepted that programs should not expect this? I would
not be ashamed to break this code in a new implementation of Ruby.

-Patrick

I read recently a flaw I did not previously know about…

"Blocks (closures) in Ruby do not introduce a new scope, only methods
do.
This is generally acknowledged as a flaw by the Ruby community but is
left
in for backwards compatibility. For example:

x = 0
[1,2,3].each{|x| print x}
#x now equals 3"

I sympathize with you in a way, Patrick, but I
do find your choice of language a little strong.

I’m not certain it’s “generally acknowledged as
a flaw” (nor where you are quoting).

There is certainly some disagreement as to how
some issues ought to be handled. Matz himself
has said he made mistakes in this area; I’m only
willing to consider something a true design flaw
when he says it is.

Of course this is just wrong. I am hoping no one expects an
implementation of Ruby to actually behave this way.

It surprised me at first, but I have had more than
three years to get used to it.

Don’t make the mistake of thinking that the list
between the bars is like a method’s formal parameter
list. There are some similarities. But it isn’t a
formal parameter list, never was, and (I’m guessing)
never will be.

For example, have you considered the fact that any
“assignable” entity can be placed there, for example,
a writable attribute?

scrollbar = ScrollBar.new(…)
(0…100).each {|scrollbar.percent| sleep(0.1) }

Is it generally accepted that programs should not expect this? I would
not be ashamed to break this code in a new implementation of Ruby.

As I said, I’ve gotten used to it. I do sometimes
depend on this behavior, usually when I want to save
a value after a loop terminates normally or abnormally.
It’s a minor thing, but it saves an extra variable.

Of course, it goes without saying that however Matz
decides to reconcile all these issues, I will simply
learn it and live with it. I suppose we all shall.

Hal

···

----- Original Message -----
From: “Patrick Logan” patrickdlogan@attbi.com
Newsgroups: comp.lang.ruby
To: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Tuesday, February 11, 2003 10:21 PM
Subject: Lexical scope and closures

Hi –

···

On Wed, 12 Feb 2003, Patrick Logan wrote:

I read recently a flaw I did not previously know about…

"Blocks (closures) in Ruby do not introduce a new scope, only methods
do.
This is generally acknowledged as a flaw by the Ruby community but is
left
in for backwards compatibility. For example:

x = 0
[1,2,3].each{|x| print x}
#x now equals 3"

Of course this is just wrong. I am hoping no one expects an
implementation of Ruby to actually behave this way.

Is it generally accepted that programs should not expect this? I would
not be ashamed to break this code in a new implementation of Ruby.

Let us know if you need help choosing a name for your new language :slight_smile:

David


David Alan Black
home: dblack@candle.superlink.net
work: blackdav@shu.edu
Web: http://pirate.shu.edu/~blackdav

I read recently a flaw I did not previously know about...

"Blocks (closures) in Ruby do not introduce a new scope, only methods
do.

It's not actually true. Blocks do introduce a new scope, iff the variable
name has not been used already. So

  # no previous use of 'x'
  [1,2,3].each{|x| print x}

creates a local instance of 'x' for each time the block executes. This is an
important distinction where the block is being run as a thread, or is
recursive.

This is generally acknowledged as a flaw by the Ruby community but is
left
in for backwards compatibility. For example:

x = 0
[1,2,3].each{|x| print x}
#x now equals 3"

Matz is thinking of changing the behaviour for a future version of Ruby as
follows:

(1) block parameters are always locally scoped (so '|x|' in that example
will behave as if the outside 'x' did not exist, although a warning will be
printed because of the re-use of the name; but x will still be 0 after the
iterator has finished)

(2) variables introduced _within_ a block are always scoped to the entire
method, not the block. e.g.

[1,2,3].each{|x| j = x}

will behave the same as

j = nil
[1,2,3].each{|x| j = x}

and hence the value of j will persist after the iterator terminates.

(3) some other syntax will be introduced for cases where you really need j
to be a block-local variable (e.g. if this block calls itself recursively or
is a thread). This might be something like:

[1,2,3].each{|x|
   local { |j|
     j = x
   }
}

This has been discussed at great length :slight_smile:

Regards,

Brian.

···

On Wed, Feb 12, 2003 at 01:21:40PM +0900, Patrick Logan wrote:

I know, but let me catch my chance to state that
I want sugar for the above!
(never thought I’d ask for that at the time I admired C’s terse syntax,
but I’m requesting terseness, anyway: the above involves lots of typing
and another indentation level)

···

On Thu, Feb 13, 2003 at 08:11:53AM +0900, Brian Candler wrote:

(3) some other syntax will be introduced for cases where you really need j
to be a block-local variable (e.g. if this block calls itself recursively or
is a thread). This might be something like:

[1,2,3].each{|x|
local { |j|
j = x
}
}

This has been discussed at great length :slight_smile:


_ _

__ __ | | ___ _ __ ___ __ _ _ __
'_ \ / | __/ __| '_ _ \ / ` | ’ \
) | (| | |
__ \ | | | | | (| | | | |
.__/ _,
|_|/| || ||_,|| |_|
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

Footnotes are for things you believe don’t really belong in LDP manuals,
but want to include anyway.
– Joel N. Weber II discussing the ‘make’ chapter of LPG

Matz is thinking of changing the behaviour for a future version of Ruby as
follows:

(1) block parameters are always locally scoped (so ‘|x|’ in that example
will behave as if the outside ‘x’ did not exist, although a warning will
be
printed because of the re-use of the name; but x will still be 0 after the
iterator has finished)

I didn’t realize that. So we ARE breaking
old code, eh?

Hal

···

----- Original Message -----
From: “Brian Candler” B.Candler@pobox.com
To: “ruby-talk ML” ruby-talk@ruby-lang.org; patrickdlogan@attbi.com
Cc: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, February 12, 2003 5:11 PM
Subject: Re: Lexical scope and closures

dblack@candle.superlink.net wrote:

I would
not be ashamed to break this code in a new implementation of Ruby.

Let us know if you need help choosing a name for your new language :slight_smile:

Exactly. I think my second round of interest in Ruby lasted about 24
hours. 8^)

No need for new language… just back to Scheme where idealists can
live safe and sound!

-Patrick

Yes. That's one reason for the warning; the other reason is because Matz
finds the practice of 'shadowing' distasteful :slight_smile:

It will also break old code where it depends on a variable introduced in a
block being local, e.g.

   Thread.new {
     i = 0
     ... use i locally
   }

I don't remember seeing a warning for this situation being discussed, but it
would seem logical to do so.

What you gain: probably less surprising behaviour. "a = 0" always binds to
the method scope (whether or not it is inside a block), and "|a|" is always
block-local (whether or not 'a' has been used before).

Regards,

Brian.

···

On Thu, Feb 13, 2003 at 08:27:51AM +0900, Hal E. Fulton wrote:

> Matz is thinking of changing the behaviour for a future version of Ruby as
> follows:
>
> (1) block parameters are always locally scoped (so '|x|' in that example
> will behave as if the outside 'x' did not exist, although a warning will
be
> printed because of the re-use of the name; but x will still be 0 after the
> iterator has finished)

I didn't realize that. So we ARE breaking
old code, eh?

I didn’t realize that. So we ARE breaking
old code, eh?

Yes. That’s one reason for the warning; the other reason is because Matz
finds the practice of ‘shadowing’ distasteful :slight_smile:

I never really understood the term “shadowing.” I thought
he meant that a local assignment inside a block resulting in a
redefinition was distasteful. Apparently that’s the exact opposite
of what he meant.

What happens to the case where obj has a meth= method, and
we put obj.meth between vertical bars?

And how will I save the value of an iterated variable? Will I have
to assign it to a var in the outer scope as soon as I enter the
block?

It will also break old code where it depends on a variable introduced in a
block being local, e.g.

Thread.new {
i = 0
… use i locally
}

I don’t remember seeing a warning for this situation being discussed, but
it
would seem logical to do so.

Hmm, even more confusing. But perhaps you could simply add
your block-local variable(s) to the list: Thread.new {|i|…

What you gain: probably less surprising behaviour. “a = 0” always binds to
the method scope (whether or not it is inside a block), and “|a|” is
always
block-local (whether or not ‘a’ has been used before).

Less surprising, but an imperfect solution IMO.

Still, none of the solutions has seemed perfect to me.

It seems that Matz’s INVIOLABLE rule is to keep Ruby
declarationless. Every solution I saw (I think) that
kept all functionality involved some kind of declaration
mechanism.

Hal

···

----- Original Message -----
From: “Brian Candler” B.Candler@pobox.com
To: “ruby-talk ML” ruby-talk@ruby-lang.org; hal9000@hypermetrics.com
Cc: “ruby-talk ML” ruby-talk@ruby-lang.org
Sent: Wednesday, February 12, 2003 5:50 PM
Subject: Re: Lexical scope and closures

Hi,

What happens to the case where obj has a meth= method, and
we put obj.meth between vertical bars?

This will cause warning first, then eventually become error in the far
future version. Block parameters will be more like method arguments.

And how will I save the value of an iterated variable? Will I have
to assign it to a var in the outer scope as soon as I enter the
block?

You have to assign to a variable from a block parameter.

It will also break old code where it depends on a variable introduced in a
block being local, e.g.

Thread.new {
i = 0
… use i locally
}

I don’t remember seeing a warning for this situation being discussed, but
it
would seem logical to do so.

Hmm, even more confusing. But perhaps you could simply add
your block-local variable(s) to the list: Thread.new {|i|…

Yes, it’s possible, even now.

It seems that Matz’s INVIOLABLE rule is to keep Ruby
declarationless. Every solution I saw (I think) that
kept all functionality involved some kind of declaration
mechanism.

Yes, for variable declarations. But I’m always open for new ideas.

						matz.
···

In message “Re: Lexical scope and closures” on 03/02/13, “Hal E. Fulton” hal9000@hypermetrics.com writes: