Ideas for an RCR: variable locality

I would like to start this thread with end goal being to create
an RCR to control variable locality in the language (or
determine that there is already a reasonable alternative).

Problem: Within a method you can't reuse a variable name for a
local variable. Some changes in ruby 2 may help the issue, but
also hurt it in another way.

Example: For performance reasons, I'm having my package
(grammar - generic parser), generate flattened (reduce
calls/stack depth) code. As long as each "method" (to be
flattened) is simple enough that it doesn't require local
variables I'm OK. But as soon one of these need a local
variable, I'm in trouble - that variable could step on the toes
of another including one just like it that it calls/flattens.

Possible solutions:

1. Do nothing in the language. Ruby coders should architect
around the problem and accept any limitations. A possible
solution to the above problem would be to use a stack (Array)
for each of these local variables to manually get locality - or
ignore performance issues.

2. Take advantage of the fact that in Ruby 2 block arguments
are always local. A localize method could be created that call
a block and that block would make the variables that it wanted
local arguments to the block. Unfortunately, this solution
doesn't help the performance issue above - it worsens it using
at least 2 call levels.

    x = 0
    a,b = 1,2
    z = localize { |x,y| # doesn't modify outside x
      x = a+b # use a and b from outside
      y = a-b
      x*y
    }

3. Use "def" to make a method (dummy unused name - _local) on
the fly and call it. Any variables that the code needed would
have to be passed in as arguments since all variables inside
would be local. Sort of an opposite approach #2 where the
arguments are local and everything else inside has the same
scope. This is doable with no change to the language, but is
quite ugly and has performance issues.

    x = 0
    a,b = 1,2
    def _local(a,b)
      x = a+b
      y = a-b
      x*y
    end
    z = _local(a,b)

4. Have a new block syntax to localize variables inside - maybe
{{ ... }} instead of { ... }. Too be more convenient than #3,
you'd want an easy way to grab variables in the containing
scope. When the code tries to read a local variable not yet
defined, it would get the value from the variable of the same
name in the containing scope.

    x = 0
    a,b = 1,2
    z = lambda {{
      # all variables inside here are local
      # initialize a/b from outside since not defined
      x = a+b
      y = a-b
      x*y
    }}.call # could have a method do the call for you

5. New localizing construct. This construct would be to
begin/end as the above #4 {{...}} would be to plain blocks.
The same handling of undefined local variables would occur
(intialize from outside).

    x = 0
    a,b = 1,2
    # reuse module keyword to prevent new keyword conflicts
    z = module
      x = a+b
      y = a-b
      x*y
    end

6. Make "module" (and probably "class" and "def") handle
reading an undefined local variable like #4 and #5 (instead of
raising an exception immediately, try initializing it from the
containing scope). With this, we could use an unused dummy
module name to solve the problem at hand (in addition to adding
flexibility to do other things):

    x = 0
    a,b = 1,2
    z = module Dummy
      x = a+b
      y = a-b
      x*y
    end

Personally, I'd like to see #4, #5, and #6, but any of those 3
might do (#4 if there was a fast builtin block evaluator - no
additional stack depth).

Any opinions on the topic? Any other ideas?

···

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

Peter and I have discussed for Suby. We haven't had a conclusion. I
last proposed:

  z = lambda [
     ...
  ]

And a way to share vars from the above scope specifically:

  z = lambda [
     share :x
  ]

Since hash and block share literal deliminators, it only seems fair
that array do the same :wink:

T.

Problem: Within a method you can't reuse a variable name for a
local variable. Some changes in ruby 2 may help the issue, but
also hurt it in another way.

Example: For performance reasons, I'm having my package
(grammar - generic parser), generate flattened (reduce
calls/stack depth) code. As long as each "method" (to be
flattened) is simple enough that it doesn't require local
variables I'm OK. But as soon one of these need a local
variable, I'm in trouble - that variable could step on the toes
of another including one just like it that it calls/flattens.

Possible solutions:

    x = 0
    a,b = 1,2
    z = localize { |x,y| # doesn't modify outside x
      x = a+b # use a and b from outside
      y = a-b
      x*y
    }

Any opinions on the topic? Any other ideas?

It seems that all of your suggests require a change to Ruby, so, keeping that in mind...

Something like:

     x = 0
     a,b = 1,2
     z = { |x, y|
       x = a + b
       y = a - b
       x * y
     }

would be my preference, but that would break existing code. So maybe something like:

     x = 0
     a,b = 1,2
     z = %M{ |x, y|
       x = a + b
       y = a - b
       x * y
     }

(the letter in the %M doesn't matter, but that's the idea)

I'll point out that this is a very similar problem to what languages with advanced macro capabilities (e.g. Common Lisp) have had to solve. The mechanism CL used is 'quasi-quote' with a syntactic abbreviation of '`' (a back quote). Inside quasi-quoted text if a ',' is encountered the value of that variable is substituted in.

Anyway, so this got me thinking along those lines. This code works in Ruby right now...

     module Gensym
       @@gensym_count = 0
       def Gensym.gensym(prefix="gensym")
         #generates a unique name with the given prefix
         @@gensym_count += 1
         return sprintf("%s_%s", prefix, @@gensym_count)
       end
     end

     def compute_z_macro(x=Gensym.gensym, y=Gensym.gensym)
       return %Q{
         #{x} = a + b
         #{y} = a - b
         #{x} * #{y}
       }
     end

     def go
       x = 0
       a,b = 1,2
       z = eval compute_z_macro
       printf("x = %d, z = %d\n", x, z)

       z = eval compute_z_macro("x")
       printf("x = %d, z = %d\n", x, z)

       puts compute_z_macro
       puts compute_z_macro("x")
     end

     go

There are no local variables created when the eval is executed (this is very good)

And with some changes to Ruby (defmacro, automatic call to gensym if no parameter value provided, using the ',' notation rather than #{}, %M, automatic call to eval when the 'call' of the macro happens, not-evaluating parameters to the macro)

     defmacro compute_z(x, y)
       return %M{
         ,x = a + b
         ,y = a - b
         ,x * ,y
       }
     end

     def go
       x = 0
       a,b = 1,2
       z = compute_z
       printf("x = %d, z = %d\n", x, z)

       z = compute_z(x)
       printf("x = %d, z = %d\n", x, z)
     end

Anyway, you have macros at the same time. A little far from what you were asking but...

Cheers,
Bob

···

On Oct 1, 2005, at 12:20 PM, Eric Mahurin wrote:

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

----
Bob Hutchison -- blogs at <http://www.recursive.ca/hutch/>
Recursive Design Inc. -- <http://www.recursive.ca/>
Raconteur -- <http://www.raconteur.info/>

Eric Mahurin wrote:

I would like to start this thread with end goal being to create
an RCR to control variable locality in the language (or
determine that there is already a reasonable alternative).

Problem: Within a method you can't reuse a variable name for a
local variable. Some changes in ruby 2 may help the issue, but
also hurt it in another way.

Example: For performance reasons, I'm having my package
(grammar - generic parser), generate flattened (reduce
calls/stack depth) code. As long as each "method" (to be
flattened) is simple enough that it doesn't require local
variables I'm OK. But as soon one of these need a local
variable, I'm in trouble - that variable could step on the toes
of another including one just like it that it calls/flattens.

Could you perhaps offer a reduced code example? Your problem
description makes no sense (though probably due to fault of
mine).

> [snip solutions]

E

···

--
No-one expects the Solaris POSIX implementation!

Wouldn't it make sense to let the most common wanted behaviour be the
default? That is, letting the block inherit the variables but letting it be
possible to not share as well, if that is wanted?

···

On 10/1/05, Trans <transfire@gmail.com> wrote:

z = lambda [
share :x
]

And a way to share vars from the above scope specifically:

  z = lambda [
     share :x
  ]

I was hoping to not bring in any type of variable declaration
syntax (like perl's my/our/local) into play. I was also
thinking that this type of block would have no write access to
the variables in the surrounding scope (block would need to
return data instead). You could give read-only access when the
block accesses one of its variables not yet assigned to (get
the value from the containing scope):

x = 1+2
z = lambda {{
  # becomes equivalent to y = (x=3) when compiled
  y = x
}}

Hopefully this could be done in such a way so that once that
block is created it doesn't need access to its containing scope
anymore and you don't have to worry about the block preventing
GCing stuff in that scope (as you do with normal blocks).
Although you wouldn't be able to get the Binding of this type
of block, you would still want to have a way to get the file,
line, (and column?) of where the block was created.

Since hash and block share literal deliminators, it only
seems fair
that array do the same :wink:

But [] is also an operator and {} is not. To use [] for a
different type of block, you'd have to differentiate between it
and the [] operator based on spacing - which I think is bad.

···

--- Trans <transfire@gmail.com> wrote:

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005
http://mail.yahoo.com

Here's one - a poor man's macro facility. Let's say a macro is
just a lambda that returns a string. You just eval it when you
need to execute the code for that macro.

plus = lambda { |a,b| "(#{a}+#{b})" }
# will have to re-evaluate a or b if they are an expression
min1 = lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
# use local variables to prevent re-evaluation
min2 = lambda { |a,b| "(a=#{a};b=#{b};a<b ? a : b)" }

# y+z may get evaluated twice
min1["x",plus["y","z"]]
# => "(x<(y+z) ? x : (y+z))"

# y+z may evaluated once
min2["x",plus["y","z"]]
# => "(a=x;b=(y+z);a<b ? a : b)"

# need to localize a/b for the inner min2
min2["x",min2["y","z"]]
# => "(a=x;b=(a=y;b=z;a<b ? a : b);a<b ? a : b)"

See the problem on this last example? We really need to
localize a and b. There isn't a good facility to do this.

···

--- ES <ruby-ml@magical-cat.org> wrote:

Eric Mahurin wrote:
> I would like to start this thread with end goal being to
create
> an RCR to control variable locality in the language (or
> determine that there is already a reasonable alternative).
>
> Problem: Within a method you can't reuse a variable name
for a
> local variable. Some changes in ruby 2 may help the issue,
but
> also hurt it in another way.
>
> Example: For performance reasons, I'm having my package
> (grammar - generic parser), generate flattened (reduce
> calls/stack depth) code. As long as each "method" (to be
> flattened) is simple enough that it doesn't require local
> variables I'm OK. But as soon one of these need a local
> variable, I'm in trouble - that variable could step on the
toes
> of another including one just like it that it
calls/flattens.

Could you perhaps offer a reduced code example? Your problem
description makes no sense (though probably due to fault of
mine).

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005
http://mail.yahoo.com

Eric Mahurin wrote:

> And a way to share vars from the above scope specifically:
>
> z = lambda [
> share :x
> ]

I was hoping to not bring in any type of variable declaration
syntax (like perl's my/our/local) into play.

I undertsnad but then you start getting into more syntax hacks like {

x,y; z|... }. That;s even worse. At least the above is simple and

clear.

I was also
thinking that this type of block would have no write access to
the variables in the surrounding scope (block would need to
return data instead).

Yes, I am too. You'd have to use #share to open a variable up.

You could give read-only access when the
block accesses one of its variables not yet assigned to (get
the value from the containing scope):

x = 1+2
z = lambda {{
  # becomes equivalent to y = (x=3) when compiled
  y = x
}}

That's cool. So you'd really only need a way to "send it out". hmmm..
#share cuold work for that, it's would then be more like #return
instead of a declaration.

Hopefully this could be done in such a way so that once that
block is created it doesn't need access to its containing scope
anymore and you don't have to worry about the block preventing
GCing stuff in that scope (as you do with normal blocks).
Although you wouldn't be able to get the Binding of this type
of block, you would still want to have a way to get the file,
line, (and column?) of where the block was created.

> Since hash and block share literal deliminators, it only
> seems fair
> that array do the same :wink:

But [] is also an operator and {} is not. To use [] for a
different type of block, you'd have to differentiate between it
and the [] operator based on spacing - which I think is bad.

That's true. But hey let's open up {} as an operator too. I'm not
afraid of the spacebar! Besides I never put spaces before my []
operators anyway, and really who does?

T.

···

--- Trans <transfire@gmail.com> wrote:

You need gensym as lisp has (and I had in a previous example), in which case you get this back on the last example, and there is no problem:

(a_1=x;b_2=(a_3=y;b_4=z;a_3<b_4 ? a_3 : b_4);a_1<b_2 ? a_1 : b_2)

···

On Oct 1, 2005, at 4:55 PM, Eric Mahurin wrote:

--- ES <ruby-ml@magical-cat.org> wrote:

Eric Mahurin wrote:

I would like to start this thread with end goal being to

create

an RCR to control variable locality in the language (or
determine that there is already a reasonable alternative).

Problem: Within a method you can't reuse a variable name

for a

local variable. Some changes in ruby 2 may help the issue,

but

also hurt it in another way.

Example: For performance reasons, I'm having my package
(grammar - generic parser), generate flattened (reduce
calls/stack depth) code. As long as each "method" (to be
flattened) is simple enough that it doesn't require local
variables I'm OK. But as soon one of these need a local
variable, I'm in trouble - that variable could step on the

toes

of another including one just like it that it

calls/flattens.

Could you perhaps offer a reduced code example? Your problem
description makes no sense (though probably due to fault of
mine).

Here's one - a poor man's macro facility. Let's say a macro is
just a lambda that returns a string. You just eval it when you
need to execute the code for that macro.

plus = lambda { |a,b| "(#{a}+#{b})" }
# will have to re-evaluate a or b if they are an expression
min1 = lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
# use local variables to prevent re-evaluation
min2 = lambda { |a,b| "(a=#{a};b=#{b};a<b ? a : b)" }

# y+z may get evaluated twice
min1["x",plus["y","z"]]
# => "(x<(y+z) ? x : (y+z))"

# y+z may evaluated once
min2["x",plus["y","z"]]
# => "(a=x;b=(y+z);a<b ? a : b)"

# need to localize a/b for the inner min2
min2["x",min2["y","z"]]
# => "(a=x;b=(a=y;b=z;a<b ? a : b);a<b ? a : b)"

See the problem on this last example? We really need to
localize a and b. There isn't a good facility to do this.

----
Bob Hutchison -- blogs at <http://www.recursive.ca/hutch/>
Recursive Design Inc. -- <http://www.recursive.ca/>
Raconteur -- <http://www.raconteur.info/>

Eric Mahurin wrote:

Eric Mahurin wrote:

I would like to start this thread with end goal being to

create

an RCR to control variable locality in the language (or
determine that there is already a reasonable alternative).

Problem: Within a method you can't reuse a variable name

for a

local variable. Some changes in ruby 2 may help the issue,

but

also hurt it in another way.

Example: For performance reasons, I'm having my package
(grammar - generic parser), generate flattened (reduce
calls/stack depth) code. As long as each "method" (to be
flattened) is simple enough that it doesn't require local
variables I'm OK. But as soon one of these need a local
variable, I'm in trouble - that variable could step on the

toes

of another including one just like it that it

calls/flattens.

Could you perhaps offer a reduced code example? Your problem
description makes no sense (though probably due to fault of
mine).

Here's one - a poor man's macro facility. Let's say a macro is
just a lambda that returns a string. You just eval it when you
need to execute the code for that macro.

plus = lambda { |a,b| "(#{a}+#{b})" }
# will have to re-evaluate a or b if they are an expression
min1 = lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
# use local variables to prevent re-evaluation
min2 = lambda { |a,b| "(a=#{a};b=#{b};a<b ? a : b)" }

# y+z may get evaluated twice
min1["x",plus["y","z"]]
# => "(x<(y+z) ? x : (y+z))"

# y+z may evaluated once
min2["x",plus["y","z"]]
# => "(a=x;b=(y+z);a<b ? a : b)"

# need to localize a/b for the inner min2
min2["x",min2["y","z"]]
# => "(a=x;b=(a=y;b=z;a<b ? a : b);a<b ? a : b)"

See the problem on this last example? We really need to
localize a and b. There isn't a good facility to do this.

Well, I would have to say in that case the main problem is
the design itself.

However, I think I see the issue: you mean *block-local* variables,
right, not regular local variables? As in,

   foo = 5
   bar = lambda {|foo| foo += 1} # Should be a local foo, not the above?

E

···

--- ES <ruby-ml@magical-cat.org> wrote:

Eric Mahurin wrote:
>
>
>>Eric Mahurin wrote:
>>
>>>I would like to start this thread with end goal being to
>>
>>create
>>
>>>an RCR to control variable locality in the language (or
>>>determine that there is already a reasonable alternative).
>>>
>>>Problem: Within a method you can't reuse a variable name
>>
>>for a
>>
>>>local variable. Some changes in ruby 2 may help the
issue,
>>
>>but
>>
>>>also hurt it in another way.
>>>
>>>Example: For performance reasons, I'm having my package
>>>(grammar - generic parser), generate flattened (reduce
>>>calls/stack depth) code. As long as each "method" (to be
>>>flattened) is simple enough that it doesn't require local
>>>variables I'm OK. But as soon one of these need a local
>>>variable, I'm in trouble - that variable could step on the
>>
>>toes
>>
>>>of another including one just like it that it
>>
>>calls/flattens.
>>
>>Could you perhaps offer a reduced code example? Your
problem
>>description makes no sense (though probably due to fault of
>>mine).
>
>
> Here's one - a poor man's macro facility. Let's say a
macro is
> just a lambda that returns a string. You just eval it when
you
> need to execute the code for that macro.
>
> plus = lambda { |a,b| "(#{a}+#{b})" }
> # will have to re-evaluate a or b if they are an expression
> min1 = lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
> # use local variables to prevent re-evaluation
> min2 = lambda { |a,b| "(a=#{a};b=#{b};a<b ? a : b)" }
>
> # y+z may get evaluated twice
> min1["x",plus["y","z"]]
> # => "(x<(y+z) ? x : (y+z))"
>
> # y+z may evaluated once
> min2["x",plus["y","z"]]
> # => "(a=x;b=(y+z);a<b ? a : b)"
>
> # need to localize a/b for the inner min2
> min2["x",min2["y","z"]]
> # => "(a=x;b=(a=y;b=z;a<b ? a : b);a<b ? a : b)"
>
>
> See the problem on this last example? We really need to
> localize a and b. There isn't a good facility to do this.

Well, I would have to say in that case the main problem is
the design itself.

I just wanted to give a simple example where having some kind
of local variable facility would easily solve the problem. In
many other cases, you can simply choose an unused name to solve
the problem (the case you have below).

However, I think I see the issue: you mean *block-local*
variables,
right, not regular local variables? As in,

   foo = 5
   bar = lambda {|foo| foo += 1} # Should be a local foo,
not the above?

This particular issue should be fixed in Ruby 2 from my
understanding - block arguments will ALWAYS be local. I'm
really talking about localizing other variables in a block and
localizing variables in something like a begin/end block.

So, in the example above, my module/end proposal would look
something like this:

min2 = lambda { |a,b| "module;a=#{a};b=#{b};a<b ? a : b;end" }

min2["x",min2["y","z"]]
# => "module;a=x;b=module;a=y;b=z;a<b ? a : b;end;a<b ? a :
b;end"

Again, my module/end proposal would localize all variables
inside, but would use an external value when reading a local
variable not yet defined. I proposed to reuse "module" to not
create a new keyword, but something else could be used.

And on the block side, I proposed a new block format ({{...}})
which would do the same for variables inside the block. In
ruby 2, the default block will have all variables scoped at the
same as what contains the block.

···

--- ES <ruby-ml@magical-cat.org> wrote:

> --- ES <ruby-ml@magical-cat.org> wrote:

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005
http://mail.yahoo.com

Yes, something like this would work (using your Gensym module):

min2 = lambda { |a,b|
  v1 = Gensym.gensym
  v2 = Gensym.gensym
  "(#{v1}=#{a};#{v2}=#{b};#{v1}<#{v2} ? #{v1} : #{v2})"
}

Here are the disadvantages of this compared to having ruby do
local variables:

- not as natural/uglier
- clutters the namespace/variable table
- prevents GC from freeing objects that these variables
reference

···

--- Bob Hutchison <hutch@recursive.ca> wrote:

On Oct 1, 2005, at 4:55 PM, Eric Mahurin wrote:
> Here's one - a poor man's macro facility. Let's say a
macro is
> just a lambda that returns a string. You just eval it when
you
> need to execute the code for that macro.
>
> plus = lambda { |a,b| "(#{a}+#{b})" }
> # will have to re-evaluate a or b if they are an expression
> min1 = lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
> # use local variables to prevent re-evaluation
> min2 = lambda { |a,b| "(a=#{a};b=#{b};a<b ? a : b)" }
>
> # y+z may get evaluated twice
> min1["x",plus["y","z"]]
> # => "(x<(y+z) ? x : (y+z))"
>
> # y+z may evaluated once
> min2["x",plus["y","z"]]
> # => "(a=x;b=(y+z);a<b ? a : b)"
>
> # need to localize a/b for the inner min2
> min2["x",min2["y","z"]]
> # => "(a=x;b=(a=y;b=z;a<b ? a : b);a<b ? a : b)"
>
>
> See the problem on this last example? We really need to
> localize a and b. There isn't a good facility to do this.
>
>

You need gensym as lisp has (and I had in a previous
example), in
which case you get this back on the last example, and there
is no
problem:

(a_1=x;b_2=(a_3=y;b_4=z;a_3<b_4 ? a_3 : b_4);a_1<b_2 ? a_1 :
b_2)

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005
http://mail.yahoo.com

Here's one - a poor man's macro facility. Let's say a

macro is

just a lambda that returns a string. You just eval it when

you

need to execute the code for that macro.

plus = lambda { |a,b| "(#{a}+#{b})" }
# will have to re-evaluate a or b if they are an expression
min1 = lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
# use local variables to prevent re-evaluation
min2 = lambda { |a,b| "(a=#{a};b=#{b};a<b ? a : b)" }

# y+z may get evaluated twice
min1["x",plus["y","z"]]
# => "(x<(y+z) ? x : (y+z))"

# y+z may evaluated once
min2["x",plus["y","z"]]
# => "(a=x;b=(y+z);a<b ? a : b)"

# need to localize a/b for the inner min2
min2["x",min2["y","z"]]
# => "(a=x;b=(a=y;b=z;a<b ? a : b);a<b ? a : b)"

See the problem on this last example? We really need to
localize a and b. There isn't a good facility to do this.

You need gensym as lisp has (and I had in a previous
example), in
which case you get this back on the last example, and there
is no
problem:

(a_1=x;b_2=(a_3=y;b_4=z;a_3<b_4 ? a_3 : b_4);a_1<b_2 ? a_1 :
b_2)

Yes, something like this would work (using your Gensym module):

min2 = lambda { |a,b|
  v1 = Gensym.gensym
  v2 = Gensym.gensym
  "(#{v1}=#{a};#{v2}=#{b};#{v1}<#{v2} ? #{v1} : #{v2})"
}

Here are the disadvantages of this compared to having ruby do
local variables:

- not as natural/uglier

No argument there at all, I agree. However a little syntactic sugar would make all the difference. I made a suggestion off the top of my head as to what that might look like, but I've got no doubt that that can be improved.

You might be able to make an argument that local variables are more fundamental and that a macro system shouldn't be the way to solve the problem. Though, the problem that motivated this discussion, which I believe amounted to a problem with in-lining functions, is probably better solved by the macro system.

- clutters the namespace/variable table

Actually it doesn't in the current ruby. In the example I gave earlier the gensymed names are not visible outside the eval.

- prevents GC from freeing objects that these variables
reference

Not if they don't exist (as I think they don't)

And the advantage is that you'd have a macro system

Cheers,
Bob

···

On Oct 1, 2005, at 7:37 PM, Eric Mahurin wrote:

--- Bob Hutchison <hutch@recursive.ca> wrote:

On Oct 1, 2005, at 4:55 PM, Eric Mahurin wrote:

----
Bob Hutchison -- blogs at <http://www.recursive.ca/hutch/>
Recursive Design Inc. -- <http://www.recursive.ca/>
Raconteur -- <http://www.raconteur.info/>

Eric Mahurin wrote:

Eric Mahurin wrote:

Eric Mahurin wrote:

I would like to start this thread with end goal being to

create

an RCR to control variable locality in the language (or
determine that there is already a reasonable alternative).

Problem: Within a method you can't reuse a variable name

for a

local variable. Some changes in ruby 2 may help the

issue,

but

also hurt it in another way.

Example: For performance reasons, I'm having my package
(grammar - generic parser), generate flattened (reduce
calls/stack depth) code. As long as each "method" (to be
flattened) is simple enough that it doesn't require local
variables I'm OK. But as soon one of these need a local
variable, I'm in trouble - that variable could step on the

toes

of another including one just like it that it

calls/flattens.

Could you perhaps offer a reduced code example? Your

problem

description makes no sense (though probably due to fault of
mine).

Here's one - a poor man's macro facility. Let's say a

macro is

just a lambda that returns a string. You just eval it when

you

need to execute the code for that macro.

plus = lambda { |a,b| "(#{a}+#{b})" }
# will have to re-evaluate a or b if they are an expression
min1 = lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
# use local variables to prevent re-evaluation
min2 = lambda { |a,b| "(a=#{a};b=#{b};a<b ? a : b)" }

# y+z may get evaluated twice
min1["x",plus["y","z"]]
# => "(x<(y+z) ? x : (y+z))"

# y+z may evaluated once
min2["x",plus["y","z"]]
# => "(a=x;b=(y+z);a<b ? a : b)"

# need to localize a/b for the inner min2
min2["x",min2["y","z"]]
# => "(a=x;b=(a=y;b=z;a<b ? a : b);a<b ? a : b)"

See the problem on this last example? We really need to
localize a and b. There isn't a good facility to do this.

Well, I would have to say in that case the main problem is
the design itself.

I just wanted to give a simple example where having some kind
of local variable facility would easily solve the problem. In
many other cases, you can simply choose an unused name to solve
the problem (the case you have below).

However, I think I see the issue: you mean *block-local*
variables,
right, not regular local variables? As in,

  foo = 5
  bar = lambda {|foo| foo += 1} # Should be a local foo,
not the above?

This particular issue should be fixed in Ruby 2 from my
understanding - block arguments will ALWAYS be local. I'm
really talking about localizing other variables in a block and
localizing variables in something like a begin/end block.

So, in the example above, my module/end proposal would look
something like this:

min2 = lambda { |a,b| "module;a=#{a};b=#{b};a<b ? a : b;end" }

min2["x",min2["y","z"]]
# => "module;a=x;b=module;a=y;b=z;a<b ? a : b;end;a<b ? a :
b;end"

Again, my module/end proposal would localize all variables
inside, but would use an external value when reading a local
variable not yet defined. I proposed to reuse "module" to not
create a new keyword, but something else could be used.

And on the block side, I proposed a new block format ({{...}})
which would do the same for variables inside the block. In
ruby 2, the default block will have all variables scoped at the
same as what contains the block.

Ah.. er.

Some kind of automatic name mangling to avoid binding?

I mean, I just do not see where this would cause any problems apart
from when one is defining a block locally in which case one should
take care not to trample on the local namespace anyway. Typically
being able to access the enclosing scope is much more valuable.

This seems like a very special-use-case and would probably just
warrant a library of some sort at most.

E

···

--- ES <ruby-ml@magical-cat.org> wrote:

--- ES <ruby-ml@magical-cat.org> wrote:

--
No-one expects the Solaris POSIX implementation!

Eric Mahurin wrote:

Again, my module/end proposal would localize all variables
inside, but would use an external value when reading a local
variable not yet defined. I proposed to reuse "module" to not
create a new keyword, but something else could be used.

I don't think reusing 'module' this way is a good idea. Modules don;t
grad varaible from outside there scope.

And on the block side, I proposed a new block format ({{...}})
which would do the same for variables inside the block. In
ruby 2, the default block will have all variables scoped at the
same as what contains the block.

{{ }} is ambigous too as an empty hash in a block.

T.

Eric Mahurin wrote:

> Again, my module/end proposal would localize all variables
> inside, but would use an external value when reading a
local
> variable not yet defined. I proposed to reuse "module" to
not
> create a new keyword, but something else could be used.

I don't think reusing 'module' this way is a good idea.
Modules don;t
grad varaible from outside there scope.

I don't particularly like using the word "module" either, but I
was hoping to find a way of not adding a new keyword since
anywhere using that new keyword currently as a variable or
method name would be a problem. Another options would be
do/end, but this could get too easily confused with a block.
module/end seemed to be a pretty close fit because module/end
already localizes variables and executes code immediately.
Another option would be to have another form of begin/end -
maybe "begin:"/end. I don't know. Or give up and have a new
keyword.

> And on the block side, I proposed a new block format
({{...}})
> which would do the same for variables inside the block. In
> ruby 2, the default block will have all variables scoped at
the
> same as what contains the block.

{{ }} is ambigous too as an empty hash in a block.

Similar ambiguity to your [] proposal. It would depend on
spacing. "{{" (localized block) would be treated differently
to "{ {" (hash in a block). This could be done at the lexer -
"{{" would be a token.

It is very hard to propose anything in the block/lambda area
without something being ambiguous because of anonymous hashes.

···

--- Trans <transfire@gmail.com> wrote:

__________________________________________________
Do You Yahoo!?
Tired of spam? Yahoo! Mail has the best spam protection around
http://mail.yahoo.com

>> (a_1=x;b_2=(a_3=y;b_4=z;a_3<b_4 ? a_3 : b_4);a_1<b_2 ? a_1
:
>> b_2)
>>
> Yes, something like this would work (using your Gensym
module):
>
> min2 = lambda { |a,b|
> v1 = Gensym.gensym
> v2 = Gensym.gensym
> "(#{v1}=#{a};#{v2}=#{b};#{v1}<#{v2} ? #{v1} : #{v2})"
> }
>
> Here are the disadvantages of this compared to having ruby
do
> local variables:
>
> - not as natural/uglier

No argument there at all, I agree. However a little syntactic
sugar
would make all the difference. I made a suggestion off the
top of my
head as to what that might look like, but I've got no doubt
that that
can be improved.

You might be able to make an argument that local variables
are more
fundamental and that a macro system shouldn't be the way to
solve the
problem. Though, the problem that motivated this discussion,
which I
believe amounted to a problem with in-lining functions, is
probably
better solved by the macro system.

For my exact situation (parser generator), I don't think the
lisp-style macros would help me. My "macros" are more
object-oriented and dynamic in nature. But, I would like to
think about it a little more to see if there would be a
general-purpose macro system that could encompass the usage I
have.

> - clutters the namespace/variable table

Actually it doesn't in the current ruby. In the example I
gave
earlier the gensymed names are not visible outside the eval.

Maybe you are thinking of blocks. Looks like eval uses/makes
variables in the surrounding scope:

x,y,z = 1,-2,3
# => [1, -2, 3]
local_variables
# => ["_", "x", "y", "z"]
eval "(a_1=x;b_2=(a_3=y;b_4=z;a_3<b_4 ? a_3 : b_4);a_1<b_2 ?
a_1 : b_2)"
# -2
local_variables
# ["_", "x", "y", "z", "a_1", "b_2", "a_3", "b_4"]

> - prevents GC from freeing objects that these variables
> reference

Not if they don't exist (as I think they don't)

Looks like they do.

···

--- Bob Hutchison <hutch@recursive.ca> wrote:

__________________________________
Yahoo! Mail - PC Magazine Editors' Choice 2005
http://mail.yahoo.com