Local variables & blocks

And while we have mentioned the new scoping rules, I think the new
rules are fine. They are simplier and more regular than the existing
rules. Its a little odd to declare block local varaibles using the
"local" trick, but then it kinda neat that the language can handle that
as well without any special rules.

All this has made look back at the Abelson and Sussman book, and I find
that even low-calorie Scheme has syntactic sugar for that usage:

  (let ((<var1> <exp1>)
        (<var2> <exp2>)
        ...
   <body>)

is translated internally to

  ((lambda (<var1> <var2>...)
        <body>)
    <exp1> <exp2>...)

Similarly in Ruby, the existing usage

  {
    a=1 # block local variables
    b=2
    .. body
  }

could instead be written as

  proc { |a,b|
    .. body
  }.call(1,2)

or as

  proc { |a,b|
    a=1
    b=2
    .. body
  }.call(nil,nil)

or as

  def local
    yield
  end
  local { |a,b|
    a=1
    b=2
    .. body
  }

I expect to have to get my head round this sort of thing if using Scheme
(which is why I don't!), but not in a scripting language. OTOH I suppose I
don't really care about true block local variables unless using recursion
within a block, or using threads, both of which are advanced usage anyway.

Perhaps 'local' should at least become a predefined method of Object, so we
don't all have to keep defining it ourselves :slight_smile:

As an aside, would it be reasonable to consider a method definition as being
wrapped in a Proc object? In other words,

define myfunc(a,b,c)
  .. stuff
end

could be considered the same as the following?

define myfunc(a1,b1,c1)
  proc { |a,b,c|
    .. stuff
  }.call(a1,b1,c1)
end

If so, then there is no such thing as local variables outside of a block. It
just becomes a question of nesting: can an inner block bind to the variables
defined in an outer block? And equally can it have its own local variables
not bound to that block?

In this case, the new rules would say:
* "i=0" always binds or creates this variable in the outermost block
* "|i|" always creates a new variable bound to the current block

I think I like this. It gets rid of the "action at a distance" problem:

i=0
j=0
... 500 lines of stuff
proc { |i|
  j=101
}.call(100)

The presence or absence of the earlier i=0 and j=0 assignments makes no
difference to the behaviour of i and j within the proc. Good, that's what
used to scare me.

The binding of j to the outermost block allows the assignment of 'j' to
persist after the executing of that section of code. Good.

Recursive closures continue to work, and if you really need local variables
within them, you can use the 'local' trick. That's OK.

Thread-local variables are gone. You can still achieve them using the
'local' trick, and at least it makes it explicit that this is what you want,
or you can wrap your thread code into its own method. This is the bit most
likely to break existing code, I guess.

I think I'm sold. As long as the usage |$m| and |obj.attr| is outlawed!

And given that, I don't think there should be a mandatory shadowing warning:
only when -w is specified. Whenever I write 'proc { |i| ...}' or
'local { |i| ...}' I am explicitly saying that I want a new local 'i', very
similar to how 'def foo(i)' always gives me a local 'i'.

I just spotted one inconsistency though: take the following example.

def myfunc(a,b,c)
  d=...
  e=...
  f=...
  ...
  myfunc(x,x,x) # recursive call
end

When such a method is called recursively, 'd', 'e' and 'f' are independent
local variables for each invocation, and there's no need to define them as
such:

def myfunc(a,b,c)
  local |d,e,f| { # not needed!
    ...
  }
end

But if I did it this way, under the new rules:

def myfunc(a1,b1,c1)
  f2 = proc { |a,b,c|
    d=...
    e=...
    f=...
    ...
    f2(x,x,x) # the same recursion
  }.call(a1,b1,c1)
end

then in this case wouldn't d,e,f be statically shared between each recursive
invocation? And so you would have to explicitly define local |d,e,f| ?

Regards,

Brian.

And while we have mentioned the new scoping rules, I think the
new rules are fine.

Can someone list a synopsis of the new rules (or have I missed that?)
This thread got to be wayyyy too long to follow for me with all the
points and counterpoints.

···

=====

Yahoo IM: michael_s_campbell


Do you Yahoo!?
Yahoo! Mail Plus - Powerful. Affordable. Sign up now.

Hi,

···

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

Perhaps ‘local’ should at least become a predefined method of Object, so we
don’t all have to keep defining it ourselves :slight_smile:

There will be provided a way to use block local variables, like local
you’ve described or something similar. Don’t worry.

						matz.

The proposed new rule is that a variable introduced in a block (other than
the block parameters) is not local to the block but the enclosing scope.
So in effect you can do then:

def summer(*args)
args.each { |arg| sum += arg.to_i }
sum
end

Today you have to write

def summer(*args)
sum = 0
args.each { |arg| sum += arg.to_i }
sum
end

robert

“Michael Campbell” michael_s_campbell@yahoo.com schrieb im Newsbeitrag
news:20030204142005.32046.qmail@web12408.mail.yahoo.com

···

And while we have mentioned the new scoping rules, I think the
new rules are fine.

Can someone list a synopsis of the new rules (or have I missed that?)
This thread got to be wayyyy too long to follow for me with all the
points and counterpoints.

=====

Yahoo IM: michael_s_campbell


Do you Yahoo!?
Yahoo! Mail Plus - Powerful. Affordable. Sign up now.
http://mailplus.yahoo.com

Hi,

The proposed new rule is that a variable introduced in a block (other than
the block parameters) is not local to the block but the enclosing scope.
So in effect you can do then:

def summer(*args)
args.each { |arg| sum += arg.to_i }
sum
end

nil doesn’t have method `+'.

Today you have to write

def summer(*args)
sum = 0
args.each { |arg| sum += arg.to_i }
sum
end

You will have to write as above under the new rule.

···

At Wed, 5 Feb 2003 00:13:39 +0900, Robert Klemme wrote:


Nobu Nakada

nobu.nokada@softhome.net schrieb im Newsbeitrag
news:200302042128.h14LSKH02451@sharui.nakada.kanuma.tochigi.jp…

Hi,

The proposed new rule is that a variable introduced in a block (other
than
the block parameters) is not local to the block but the enclosing
scope.
So in effect you can do then:

def summer(*args)
args.each { |arg| sum += arg.to_i }
sum
end

nil doesn’t have method `+'.

Darn! You’re right.

Today you have to write

def summer(*args)
sum = 0
args.each { |arg| sum += arg.to_i }
sum
end

You will have to write as above under the new rule.

Alternatively:

def andy_summers(*args)
args.each { |arg| sum = sum.to_i + arg.to_i }
sum
end

:slight_smile:

Regards

robert
···

At Wed, 5 Feb 2003 00:13:39 +0900, > Robert Klemme wrote:

Hi,

···

At Wed, 5 Feb 2003 17:46:37 +0900, Robert Klemme wrote:

Alternatively:

def andy_summers(*args)
args.each { |arg| sum = sum.to_i + arg.to_i }
sum
end

:slight_smile:

It looks better as an example.


Nobu Nakada

Hi –

Alternatively:

def andy_summers(*args)
args.each { |arg| sum = sum.to_i + arg.to_i }
sum
end

Or (as of 1.7.x):

def andy_summers(*args)
args.inject(0) {|a,b| a + b}
end

:slight_smile:

(But I know that wasn’t the point.)

David

···

On Wed, 5 Feb 2003, Robert Klemme wrote:


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