Local variables & blocks

   
> I may drop ':=' part, i.e.
   
Now read the discussion about :=' and you'll have the response to your
questions.

Unfortunately, although I receive ruby-talk in digest form, there are some
weeks when I simply have to delete every day's mails. That's just how it
goes. I did read [52440], [63087] and [63100] though, and the terminology
was ambiguous to me. Does "shadowed" mean "aliased" or "hidden"?

I note an example at the end of [52440] though, which shows the block
parameter to be truly local - but then Matz says it won't be done like that
for a long time :frowning:

So it appears that the answer to my own question is "10, one day". That's
all I was looking for.

Matz could of course start generating the shadowing warnings now, even if
not changing the behaviour. That would (a) discourage people from relying on
the current behaviour, and (b) catch errors where you actually intended the
block parameter to be local.

Regards,

Brian.

Unfortunately, although I receive ruby-talk in digest form, there are some
weeks when I simply have to delete every day's mails. That's just how it
goes. I did read [52440], [63087] and [63100] though, and the terminology
was ambiguous to me. Does "shadowed" mean "aliased" or "hidden"?

It's simple :

    * all variables are local except variables found between || which are
      block local

    * you have a warning (and shadowing) if a variable found between || was
      previously declared

This give

   [0].each do |x| # `x' block local variable
      a = x # `a' local variable
      [1].each do |a| # warning shadowing variable `a'
         # a = 1, x = 0
      end
      # a = 0, x = 0
      [2].each do |x| # warning shadowing variable `x'
         # a = 0, x = 2
      end
      # a = 0, x = 0
   end
   # a = 0
   # undefined variable x

Guy Decoux

Thanks, I just wasn't sure of the definition of "shadowing". As far as I can
see it means "a completely different variable, which just happens to have
the same name as another one in an outer scope"

In C it would be:

#include <stdio.h>
int main(void)
{
int x = 1;
{
   int x = 2; /* this x 'shadows' the other one? */
   printf("%d\n",x); /* it's just a different variable */
}
printf("%d\n",x);
return 0;
}

Incidentally, gcc -Wall doesn't give any warnings with that program, since
it's a perfectly reasonable thing to do. The only reason Ruby would warn is
because it's not backwards-compatible. I would imagine that eventually this
warning would be turned off.

Regards,

Brian.

···

On Sun, Feb 02, 2003 at 09:30:12AM +0100, ts wrote:

> Unfortunately, although I receive ruby-talk in digest form, there are some
> weeks when I simply have to delete every day's mails. That's just how it
> goes. I did read [52440], [63087] and [63100] though, and the terminology
> was ambiguous to me. Does "shadowed" mean "aliased" or "hidden"?

It's simple :

    * all variables are local except variables found between || which are
      block local

    * you have a warning (and shadowing) if a variable found between || was
      previously declared

because it's not backwards-compatible. I would imagine that eventually this
warning would be turned off.

Probably I'm wrong, but I don't think that it will be possible to turn off
the warning :slight_smile:

See [ruby-talk:52462] http://www.ruby-talk.org/52462

I'm afraid I won't give you a way to turn it off. It is "quite
common" in other languages, but it does not mean Ruby must follow.
I believe local variable shadowing is a bad thing.

Guy Decoux

> I'm afraid I won't give you a way to turn it off. It is "quite
> common" in other languages, but it does not mean Ruby must follow.
> I believe local variable shadowing is a bad thing.

But it's not a local variable - it's a formal parameter to a block. At
least, that's how I look at it :slight_smile:

Regards,

Brian.

But it's not a local variable - it's a formal parameter to a block. At
least, that's how I look at it :slight_smile:

You have found where is your problem, ruby don't see it like this

pigeon% cat b.rb
#!/usr/bin/ruby
class A
   attr_accessor :a
end

$m = 12
a = A.new
a.a = 24
p a.a, $m
[[1, 2]].each {|a.a, $m| }
p a.a, $m
pigeon%

pigeon% b.rb
24
12
1
2
pigeon%

Guy Decoux

> But it's not a local variable - it's a formal parameter to a block. At
> least, that's how I look at it :slight_smile:

You have found where is your problem, ruby don't see it like this

...

[[1, 2]].each {|a.a, $m| }
p a.a, $m

Ugh! Thanks though, that helps me see how the current behaviour (|x| assigns
to existing local variable x) arises.

It's difficult to see how that could be made consistent with the proposed
new behaviour; you can't have a block-local value for 'a.a', because it's
not a variable, it's a method call.

Should the above usage be outlawed? In the same way as:

def myfunc($m)
puts $m
end
==> compile error: formal argument cannot be a global variable

Regards,

Brian.

···

On Sun, Feb 02, 2003 at 12:36:52PM +0100, ts wrote:

It's difficult to see how that could be made consistent with the proposed
new behaviour; you can't have a block-local value for 'a.a', because it's
not a variable, it's a method call.

Not really sure but I think that you'll get a warning if you put something
else than a local variable in ||

Guy Decoux

I think matz planned to use the implicit ‘:=’ for block-locals and ‘=’
for everything else, so that [1].each { |a.a| } works.

···

On Sun, Feb 02, 2003 at 09:11:31PM +0900, Brian Candler wrote:

On Sun, Feb 02, 2003 at 12:36:52PM +0100, ts wrote:

But it’s not a local variable - it’s a formal parameter to a block. At
least, that’s how I look at it :slight_smile:

You have found where is your problem, ruby don’t see it like this

[[1, 2]].each {|a.a, $m| }
p a.a, $m

Ugh! Thanks though, that helps me see how the current behaviour (|x| assigns
to existing local variable x) arises.

It’s difficult to see how that could be made consistent with the proposed
new behaviour; you can’t have a block-local value for ‘a.a’, because it’s
not a variable, it’s a method call.

Should the above usage be outlawed? In the same way as:

def myfunc($m)
puts $m
end
==> compile error: formal argument cannot be a global variable


_ _

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

Because I don’t need to worry about finances I can ignore Microsoft
and take over the (computing) world from the grassroots.
– Linus Torvalds

I think matz planned to use the implicit ':=' for block-locals and '='
for everything else, so that [1].each { |a.a| } works.

Yes, see [ruby-talk:52462]

Instead, since they are "other types of variables", you will be warned.

Guy Decoux

Hmm. Well, that's made me think. I have a suggestion which is even more
radical than Matz's:

* Define block parameters to be the same as regular assignment
* Have NO block-local scope introduced whatsoever
* That's it

For example:

   [1,2,3].each do |x|
     y = x * 10
   done
   puts x,y

will print "3" and "30", regardless of whether x and y were assigned to
previously, or what values they had if they were.

Benefits:
- No longer violates the POLS, where the persistence of x and y depends on
  whether they were defined earlier
- Mostly backwards compatible (would only fail where 'puts x,y' in the above
  example relied on that being interpreted as 'puts self.x,self.y')
- There is no shadowing, and therefore no mandatory warning required
- Officially blesses the (somewhat dubious) practice of |$m| and |a.a|
- Very simple to understand

Downside:
- May be frowned upon by computer scientists (however, the current
  behaviour, when investigated in detail, is also frowned upon)

It would be worth generating a warning (-w) if a variable is first defined
within a block, and subsequently referenced outside it. This is because
scripts written to rely on this behaviour would not work under older
versions of Ruby.

If the above is considered too clunky, then I'd rather go to the whole other
extreme: block parameters are proper formal parameters, they are named like
local variables but shadow them, and constructs like |$m| and |a.a| are
outlawed. But then we're back to the pros and cons of having other
block-local variables which aren't parameters.

The trouble with the current behaviour is it _looks_ like formal parameters
(it had me fooled, anyway), but isn't.

Regards,

Brian.

···

On Sun, Feb 02, 2003 at 01:16:02PM +0100, ts wrote:

> It's difficult to see how that could be made consistent with the proposed
> new behaviour; you can't have a block-local value for 'a.a', because it's
> not a variable, it's a method call.

Not really sure but I think that you'll get a warning if you put something
else than a local variable in ||

Downside:
- May be frowned upon by computer scientists (however, the current
  behaviour, when investigated in detail, is also frowned upon)

What do you do with Thread and Proc which *need* block local variables ?

Guy Decoux

Hmm. ISTM that if this is a problem, it will affect Matz's [63100] as well.

I just re-read the Pickaxe section on threads, and yes I agree - and I also
think it's a mess. When you assign to 'x' within a block, it has two
entirely different behaviours, depending on whether 'x' was in existence
previously or not.

You can avoid this by wrapping up the thread code in a method:

while (session = server.accept)
  session.extend MyModule
  Thread.new do
    session.domystuff # contains its own local variables
  end
end

But I can't see how to change it without breaking existing code, or indeed,
whether the two current behaviours are in fact necessary anyway. If they
are, then there's no escaping having a method to choose between them, either
implicit (as at present), or explicit, such as declaring "external i"

Now, as for Procs, I haven't used them much but I suppose the same sort of
thing applies:

  i = 1
  ...
  a = Proc.new { |x| i = x+x; i } # 'i' is some intermediate variable

  puts i
  puts a.call(3)
  puts i
  puts a.call(4)
  puts i

==> 1, 6, 6, 8, 8

Arguably, that's not what was expected if the author had forgotten about the
use of the temporary 'i' earlier in the code. But on the other hand, we
don't want to lose usage like this:

  found = false
  a.each { |i| found = true if i > 100 }
  found && puts "Got it"

I don't think that's really a problem. Under the scenario I proposed,

  a = Proc.new { |x| i = x+x; i }

would generate a single local variable i, scoped to the end of the current
method/class, and each time a.call is invoked its value will be clobbered,
but does that matter? It's only an internal transient value, its value would
have been lost between calls anyway.

So that leaves threads as the biggest problem. I did wonder whether the
_entire_ local stack frame (containing the values of all local variables,
not just block-local ones) could be duplicated for each thread. For example:

count = 0
while (session = server.accept)
  count += 1
  Thread.new do
    i = 0
    ... whatever using 'i' and 'count'
  end
end

At the moment 'count' is shared between the threads, but what if each thread
got its own local copy of 'count', initialised to the value at the time the
thread were started? This would stop threads interacting via shared local
variables - probably a good thing :slight_smile:

It would then mean that 'i' and 'count' could be treated identically: they
would both just be local variables, and the thread gets its own copy to do
with as it pleases.

Regards,

Brian.

···

On Sun, Feb 02, 2003 at 01:50:05PM +0100, ts wrote:

What do you do with Thread and Proc which *need* block local variables ?

would generate a single local variable i, scoped to the end of the current
method/class, and each time a.call is invoked its value will be clobbered,
but does that matter? It's only an internal transient value, its value would
have been lost between calls anyway.

What is the result if you have different Proc created in the same def, but
called in different threads ?

Can you predict the result ?

Guy Decoux

Hi,

What do you do with Thread and Proc which need block local variables ?

Hmm. ISTM that if this is a problem, it will affect Matz’s [63100] as well.

No. Unlike yours, mine still has sort of block local variabls.

I just re-read the Pickaxe section on threads, and yes I agree - and I also
think it’s a mess. When you assign to ‘x’ within a block, it has two
entirely different behaviours, depending on whether ‘x’ was in existence
previously or not.

I agree with you. I was insane when I designed this rule. That’s why
I propose the change. But I don’t think we can just drop block local
variables like yours, since we have Proc (some kind of anonymous
function), we need some kind of local variables of their own.

Now, as for Procs, I haven’t used them much but I suppose the same sort of
thing applies:

i = 1

a = Proc.new { |x| i = x+x; i } # ‘i’ is some intermediate variable

Unfortunately, this doesn’t work for closures. In a plain words,
global variables (from the viewpoint of a Proc) can not always replace
local variables.

						matz.
···

In message “Re: Local variables & blocks” on 03/02/02, Brian Candler B.Candler@pobox.com writes:

On Sun, Feb 02, 2003 at 01:50:05PM +0100, ts wrote:

> At the moment 'count' is shared between the threads, but what if each thread > got its own local copy of 'count', initialised to the value at the time the > thread were started? This would stop threads interacting via shared local > variables - probably a good thing :-)

this is called fork, and ipc is neither fun nor portable.

> It would then mean that 'i' and 'count' could be treated identically: they > would both just be local variables, and the thread gets its own copy to do > with as it pleases.

which would eliminate one of the biggest uses/advatages of threads :

shared job pool

pool = JobPool.new

while (not pool.done) do
Thread.new {
job = nil
pool.semaphore.syncronize {
job = pool.next_job
}
job.run
pool.semaphore.syncronize {
pool << job
}
}
end

-a

···

On Sun, 2 Feb 2003, Brian Candler wrote:

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

I said threads were still a problem :slight_smile:

A duplicate frame for each thread wouldn't suffer this - it would clobber
its own local copy. But there may still be some fundamental problem with
this waiting to be pointed out...

Regards,

Brian.

···

On Sun, Feb 02, 2003 at 03:35:36PM +0100, ts wrote:

> would generate a single local variable i, scoped to the end of the current
> method/class, and each time a.call is invoked its value will be clobbered,
> but does that matter? It's only an internal transient value, its value would
> have been lost between calls anyway.

What is the result if you have different Proc created in the same def, but
called in different threads ?

insane? i don’t think so to me this is natural as in Cs

int x;
{
x = 42;
}

vs

int x;
{
int x;
x = 42;
}

this is expected when one realizes ruby’s lhs statement do both declaration in
current scope and definition in current scope.

how about some simple means on introducing a local scope? a new
keyword/method? perhaps ‘scope/end’ and ‘{{/}}’. this could allow the
programmer to work as wished, added to your current plan one would end up with
:

[4,2].each { |c|
s << c.to_s
}
puts s # >> 42

[4,2].each {{ |c|
s << c.to_s
}}
puts s

NameError: undefined local variable or method `s’ for #Object:0x401909fc

maybe that syntax is no good, but what would be the problem of allowing
programmers to introduce scopes (most languages do)? this would allow to the
introduce your new plan, and in cases (like threads) or for people where it
might be an imperfect fit you could say ‘just introduce a scope and it will be
as before’. i guess one could simply use guy’s ‘local’ method, but it would
be nice if that were built in for people who like scopes. using this, the
default for thread might be to introduce a new scope - there are some cases
where POLS means blocks should start a new scope.

-a

···

On Sun, 2 Feb 2003, Yukihiro Matsumoto wrote:

I agree with you. I was insane when I designed this rule. That’s why I
propose the change. But I don’t think we can just drop block local
variables like yours, since we have Proc (some kind of anonymous function),
we need some kind of local variables of their own.

====================================

Ara Howard
NOAA Forecast Systems Laboratory
Information and Technology Services
Data Systems Group
R/FST 325 Broadway
Boulder, CO 80305-3328
Email: ahoward@fsl.noaa.gov
Phone: 303-497-7238
Fax: 303-497-7259
====================================

Don’t be so harsh to yourself! Nobody’s perfect. I like your proposed
change. It doesn’t offer all the flexibility that has been talked
about in the last couple of months, yet it is simple (as simple as it
can get?) and doesn’t change the feeling of the language.

I wonder how you’d design scoping rules for Ruby if you could start
from scratch. Would it be like the proposed solution or something
different?

Regards,
Pit

···

On 2 Feb 2003 at 23:40, Yukihiro Matsumoto wrote:

In message “Re: Local variables & blocks” > on 03/02/02, Brian Candler B.Candler@pobox.com writes:

When you assign to ‘x’ within a block, it has two
entirely different behaviours, depending on whether ‘x’ was in existence
previously or not.

I agree with you. I was insane when I designed this rule. That’s why
I propose the change. But I don’t think we can just drop block local
variables like yours, since we have Proc (some kind of anonymous
function), we need some kind of local variables of their own.

though it would not be backwards compatilble, yet it has some merit i think,
say if block using { } defined a new scope and those using do end did not?
–just an aside thought.

i can’t help but feel that this whole problem is so thorny b/c there’s some
fundemental assumptions that have been made in Ruby that at this point make
it hard to get past with out some sort of hack, whatever it may be.

it may be beneficial to step back and look at this. for starters i have always
felt a bit odd about the distinction between methods and variables:

def bar() 4 end
puts bar

vs.
bar = 4
puts bar

the use of bar after definition is ISOMORPHIC. the only exception being when
both froms are used, thus self becomes required to get to the method (the
infamous ambiguity)

likewise Procs being distinct from methods adds fruther to the layers of
complexity. i have often thought that doing:

class Foo
	bar = Proc { 4 }
end

or prefereably without the Proc (such that { } meant Proc inheritly)

class Foo
	bar = { 4 }
end

would make for a reasonable means of defining a method, equiv. to

class Foo
	def bar
		4
	end
end

(hence i never liked the #call method) but anyhow, what’s this to do with
local scope? well, the “pure” idea of a scope is to shelter the inner-context
from the outer-context. thus generally, no outer definition should get in,
and all such out-definitions that one might want inside an inner-scope would
have to be passed in.

bar =  4
non = { |bar| bar + 1}  # hey, these are not the same bar
puts non(bar)  # bar ain't getting in unless passed.

the only “pure OO” way to get around this would be to offer an object
reference to the previous scope.

bar = 4
non = { last_scope.bar + 1}  # now they the same
puts non

some sytatical suger might help: non = { &bar + 1 }

but the lack of distiction between method and variable make it clearer, i
think, where scopes are coming from. normally with,

class Foo
	def bar() 4 end
	def non() bar end
end

defines only a partial scope, if we wrote:

class Foo
	bar = { 4 }
	non = { bar }
end

we might actually consider if bar should actually be seen at all in non, but
this would lead to a lot of the above & use. imagine having to write:

class Foo
	def bar() 4 end
	def non() &bar end
end

so, we end up in a fight between what’s visible everywhere throughout the
scope of an object, sort of like object localized globals, and what’s truly
local to blocks and methods. that’s really the bottom line.

what syntax do you use to draw that line?

-transami

p.s. though i know that taking greater advatage of isomprophism with variables
and methods is quite a departure from today’s Ruby, i none the less think it
one to think carefully about and would like to hear what others think of it
and how it might effect the language over all.

···

On Sunday 02 February 2003 09:11 am, ahoward wrote:

how about some simple means on introducing a local scope? a new
keyword/method? perhaps ‘scope/end’ and ‘{{/}}’. this could allow the
programmer to work as wished, added to your current plan one would end up
with

maybe that syntax is no good, but what would be the problem of allowing
programmers to introduce scopes (most languages do)? this would allow to
the introduce your new plan, and in cases (like threads) or for people
where it might be an imperfect fit you could say ‘just introduce a scope
and it will be as before’. i guess one could simply use guy’s ‘local’
method, but it would be nice if that were built in for people who like
scopes. using this, the default for thread might be to introduce a new
scope - there are some cases where POLS means blocks should start a new
scope.


tom sawyer, aka transami
transami@transami.net