Parameter in a block is not local?

  • Thread starter SpringFlowers AutumnMoon
  • Start date
S

SpringFlowers AutumnMoon

I thought a iterator with a block is like an nameless function call...
so if it is a function call, the parameter is local.
but for the following, the output is surprising:

a = 1
p a

1.upto(10) {|a| p a}

p a

--------------

E:\>ruby test_iterator.rb
1
1
2
3
4
5
6
7
8
9
10
10

a is changed!

but is it true that the "a" is not global to begin with....
but then, isn't the "a" inside the block more local than the "a"
outside?

like in Pascal, i think there can be nested functions and therefore,
there will be local and then "local that is more local" than the outer
local.
 
7

7stud --

SpringFlowers said:
I thought a iterator with a block is like an nameless function call...
so if it is a function call, the parameter is local.
but for the following, the output is surprising:

Reread pickaxe2, p.51, second paragraph.
 
7

7stud --

SpringFlowers said:
I thought a iterator with a block is like an nameless function call...
so if it is a function call, the parameter is local.
but for the following, the output is surprising:

Also, I think I recall reading a post by Matz somewhere where he
shouldered the blame for that "feature" and said it was a design
mistake.
 
D

David A. Black

Hi --

I thought a iterator with a block is like an nameless function call...
so if it is a function call, the parameter is local.
but for the following, the output is surprising:

a = 1
p a

1.upto(10) {|a| p a}

p a

--------------

E:\>ruby test_iterator.rb
1
1
2
3
4
5
6
7
8
9
10
10

a is changed!

but is it true that the "a" is not global to begin with....
but then, isn't the "a" inside the block more local than the "a"
outside?

a is local, not global. The semantics of block parameters is
assignment semantics:

|a|

basically means "a = ...", in the same scope as the scope in which the
block is created. Blocks are different in that respect from methods,
and the || notation (instead of (), as in methods) is a good reminder
that they're not the same. (Not that || specifically implies
assignment semantics, but it's a reminder that there's a difference.)


David

--
Upcoming training from Ruby Power and Light, LLC:
* Intro to Ruby on Rails, Edison, NJ, October 23-26
* Advancing with Rails, Edison, NJ, November 6-9
Both taught by David A. Black.
See http://www.rubypal.com for more info!
 
A

ara.t.howard

I thought a iterator with a block is like an nameless function call...
so if it is a function call, the parameter is local.
but for the following, the output is surprising:

a = 1
p a

1.upto(10) {|a| p a}

p a

one way to think about it (not entirely correct) is this

- if the parameter exists *already* it is reused
- if not a block local var is created

this

1.upto(10){|a| p a}

p a

will throw an error - as you probably know. it's a fine line between
having function call semantics and closure ones. we all want this to
work

message = 'hello world'
42.times{ p message }

of course. so it's rather tricky. you can search the archives but i
*think* the new rule is that introducing a var like so

method{ |some_var| ...

will introduce a block local var (as you are expecting above), while
this

method{ p some_var

will not. i could be confused on this though, it really gets
confusing to not violate POLS (referring to mine here) in all cases

http://eigenclass.org/hiki.rb?Changes+in+Ruby+1.9#l7
http://www.davidflanagan.com/blog/2007_08.html#000137

and, of course, search the archives - matz has spelled out the
changes several times.

kind regards.

a @ http://codeforpeople.com/
 
D

David A. Black

a is local, not global. The semantics of block parameters is
assignment semantics:

|a|

basically means "a = ...", in the same scope as the scope in which the
block is created.

I should amend that by noting that it's a "one-way valve": if the
variable exists before the block is created, the scope is flat,
whereas if the variable doesn't exist yet, it also doesn't exist when
the block exits.

In other words, the local scope enters the block, from the left, so to
speak, bringing all its bindings with it; and it exits from the right
with only those bindings, which may or may not have been altered
during their time in the block.


David

--
Upcoming training from Ruby Power and Light, LLC:
* Intro to Ruby on Rails, Edison, NJ, October 23-26
* Advancing with Rails, Edison, NJ, November 6-9
Both taught by David A. Black.
See http://www.rubypal.com for more info!
 
P

Pat Maddox

I thought a iterator with a block is like an nameless function call...
so if it is a function call, the parameter is local.
but for the following, the output is surprising:

a = 1
p a

1.upto(10) {|a| p a}

p a

--------------

E:\>ruby test_iterator.rb
1
1
2
3
4
5
6
7
8
9
10
10

a is changed!

but is it true that the "a" is not global to begin with....
but then, isn't the "a" inside the block more local than the "a"
outside?

like in Pascal, i think there can be nested functions and therefore,
there will be local and then "local that is more local" than the outer
local.


This seems to me along the same lines as

a = 1
if true
a = 10
end
p a # => 10

Which certainly is not surprising or unwelcome.

Pat
 
W

Wolfgang Nádasi-Donner

SpringFlowers said:
a = 1
p a

1.upto(10) {|a| p a}

p a

--------------

E:\>ruby test_iterator.rb
1
1
2
3
4
5
6
7
8
9
10
10

a is changed!

!Remark only!

There will be a change for soon upcoming Ruby 1.9.1:

irb(main):001:0> a = 1
=> 1
irb(main):002:0> p a
1
=> nil
irb(main):003:0> 1.upto(10) {|a| p a}
1
2
3
4
5
6
7
8
9
10
=> 1
irb(main):004:0> p a
1
=> nil

Wolfgang Nádasi-Donner
 
R

Rick DeNatale

I thought a iterator with a block is like an nameless function call...

Similar, but not the same. Blocks are closures, so for example:

a = 1
1.times {p a}

will print 1 since a in the block refers to a in the outer scope.

so if it is a function call, the parameter is local.
but for the following, the output is surprising:

a = 1
p a

1.upto(10) {|a| p a}

p a

--------------

E:\>ruby test_iterator.rb
1
1
2
3
4
5
6
7
8
9
10
10

a is changed!

but is it true that the "a" is not global to begin with....
but then, isn't the "a" inside the block more local than the "a"
outside?

In Ruby 1.8 a block argument acts like a local in the containing scope.

This is changing in 1.9

http://eigenclass.org/hiki/Changes+in+Ruby+1.9#l8
 
S

SpringFlowers AutumnMoon

Pat said:
This seems to me along the same lines as

a = 1
if true
a = 10
end
p a # => 10

Which certainly is not surprising or unwelcome.

when compared to the following it is surprising:

a = 1
p a

def foo(a)
p a
end

for i in 1..10
foo(i)
end

p a

------------------

E:\>ruby test_iterator2.rb
1
1
2
3
4
5
6
7
8
9
10
1
 
S

SpringFlowers AutumnMoon

Rick said:

I don't know what software install Ruby 1.9 on my computer

C:\Users\Mike\ruby-1.9.0-20060415-i386-mswin32

and then if I run ruby 1.9, it gives me the same result:

C:\Users\Mike\ruby-1.9.0-20060415-i386-mswin32\bin>ruby test_iterator.rb
1
1
2
3
4
5
6
7
8
9
10
10
"=========="
"1.9.0"

---------------------------
Code


a = 1
p a

1.upto(10) {|a| p a}

p a

p "=" * 10

p RUBY_VERSION
 
W

Wolfgang Nádasi-Donner

SpringFlowers said:
I don't know what software install Ruby 1.9 on my computer

C:\Users\Mike\ruby-1.9.0-20060415-i386-mswin32

This is a very old version of Ruby 1.9. Please don't forget that Ruby
1.9(.0) is still an experimental version, that will move towards the
final Ruby 1.9.1 in December 2007 (I hope "December 2007" is the correct
date).

There are newer precompiled versions
(ruby-1.9.0-20070709-x64-mswin64_80.zip on
http://dl.ambiweb.de/mirrors/ftp.ruby-lang.org/binaries/mswin32/) or
snapshots which can be compiled (snapshot.tar.gz on
ftp://ftp.ruby-lang.org/pub/ruby/).

Wolfgang Nádasi-Donner
 
P

Pat Maddox

when compared to the following it is surprising:

a = 1
p a

def foo(a)
p a
end

for i in 1..10
foo(i)
end

p a

------------------

E:\>ruby test_iterator2.rb
1
1
2
3
4
5
6
7
8
9
10
1

Well, that's kind of the point of blocks, isn't it? They close over
any vars that are currently in scope. Methods on the other hand have
their own scope. I guess it could be confusing if you thought methods
and blocks were the same thing...but, um, they're not.

Pat
 
D

David A. Black

Hi --

when compared to the following it is surprising:

a = 1
p a

def foo(a)
p a
end

for i in 1..10
foo(i)
end

p a

------------------

E:\>ruby test_iterator2.rb
1
1
2
3
4
5
6
7
8
9
10
1

There's no relation or connection between the two. The def keyword
always starts a new local scope; the if keyword doesn't. There's no
reason to expect them to behave the same way as each other, as they
serve entirely different purposes.


David

--
Upcoming training from Ruby Power and Light, LLC:
* Intro to Ruby on Rails, Edison, NJ, October 23-26
* Advancing with Rails, Edison, NJ, November 6-9
Both taught by David A. Black.
See http://www.rubypal.com for more info!
 
M

mortee

SpringFlowers said:
when compared to the following it is surprising:

a = 1
p a

def foo(a)
p a
end

for i in 1..10
foo(i)
end

p a

------------------

E:\>ruby test_iterator2.rb
1
1
2
3
4
5
6
7
8
9
10
1

Yes, the def syntax specifically doesn't take the caller binding, AFAIK.
If you intend having methods access variables local to where they are
defined, just use define_method, which takes a block, with all of its
peculiarities, like taking the caller binding along with its local
variables.

mortee
 
S

SpringFlowers AutumnMoon

David said:
Hi --



There's no relation or connection between the two. The def keyword
always starts a new local scope; the if keyword doesn't. There's no
reason to expect them to behave the same way as each other, as they
serve entirely different purposes.

So in a way, think of a code block like a "if" statement? (no new local
scope)

Come to think about it, since

[1,2,3,4,5].each {|a| p a}

is equivalent to

for a in [1,2,3,4,5]
p a
end

so the "for" version clearly doesn't have a new scope for "a".

But in the newest Ruby 1.9, they are not equivalent any more?
 
M

mortee

SpringFlowers said:
So in a way, think of a code block like a "if" statement? (no new local
scope)

Come to think about it, since

[1,2,3,4,5].each {|a| p a}

is equivalent to

for a in [1,2,3,4,5]
p a
end

so the "for" version clearly doesn't have a new scope for "a".

But in the newest Ruby 1.9, they are not equivalent any more?

No, they aren't the same even in Ruby 1.8.x either. With the do...end or
{...} syntax, any variables assigned in the block, but inexisting at the
point of the definition of the block, are actually local to the block.

AFAIK, in 1.9, the formal parameters of the block, specified between
||'s, are considered unconditionally block-local *too*. In 1.8.x, those
are treated like any other local variables in the block: they refer to
the same as the enclosing block if pre-existing, and refer to
block-local variables otherwise.

mortee
 
M

mortee

mortee said:
SpringFlowers said:
[1,2,3,4,5].each {|a| p a}

is equivalent to

for a in [1,2,3,4,5]
p a
end

so the "for" version clearly doesn't have a new scope for "a".

But in the newest Ruby 1.9, they are not equivalent any more?

No, they aren't the same even in Ruby 1.8.x either. With the do...end or
{...} syntax, any variables assigned in the block, but inexisting at the
point of the definition of the block, are actually local to the block.

I should have pointed out specifically the difference to the for ... in
... end syntax: any local variable created (assigned to) in the latter
construct become part of the enclosing context, so you can actually
create new local variables in such a loop for later use.

mortee
 
D

David A. Black

Hi --

So in a way, think of a code block like a "if" statement? (no new local
scope)

I think the best thing is to learn about blocks as blocks, and don't
worry too much about how much they are or aren't similar to other
things. A block has the variables from the local scope in which it was
created, and can also create its own variables, but the ones it
creates exist only inside the block. An if statement is truly flat
with regard to its local scope.


David

--
Upcoming training from Ruby Power and Light, LLC:
* Intro to Ruby on Rails, Edison, NJ, October 23-26
* Advancing with Rails, Edison, NJ, November 6-9
Both taught by David A. Black.
See http://www.rubypal.com for more info!
 
7

7stud --

SpringFlowers said:
I thought a iterator with a block is like an nameless function call...

Here's another example(a modification of the example found in pickaxe2,
p 106):

if false
data = "hello"
end

(1..3).each {data = "goodbye"}

puts data

--output:--
goodbye


That is equivalent to Java's call by value--but using references and C++
reference semantics, and the flat scope is converted to spheric scope
with assignment semantics applying throughout any call by value passing.
:)
 

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
474,269
Messages
2,571,349
Members
48,028
Latest member
Rigor4

Latest Threads

Top