ideas for an RCR: variable locality

Discussion in 'Ruby' started by Eric Mahurin, Oct 1, 2005.

  1. Eric Mahurin

    Eric Mahurin Guest

    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 =3D 0
    a,b =3D 1,2
    z =3D localize { |x,y| # doesn't modify outside x
    x =3D a+b # use a and b from outside
    y =3D 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 =3D 0
    a,b =3D 1,2
    def _local(a,b)
    x =3D a+b
    y =3D a-b
    x*y
    end
    z =3D _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 =3D 0
    a,b =3D 1,2
    z =3D lambda {{
    # all variables inside here are local
    # initialize a/b from outside since not defined
    x =3D a+b
    y =3D 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.=20
    The same handling of undefined local variables would occur
    (intialize from outside).

    x =3D 0
    a,b =3D 1,2
    # reuse module keyword to prevent new keyword conflicts
    z =3D module=20
    x =3D a+b
    y =3D 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 =3D 0
    a,b =3D 1,2
    z =3D module Dummy
    x =3D a+b
    y =3D 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=20
    http://mail.yahoo.com=20
    Eric Mahurin, Oct 1, 2005
    #1
    1. Advertising

  2. Eric Mahurin

    Trans Guest

    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 ;-)

    T.
    Trans, Oct 1, 2005
    #2
    1. Advertising

  3. ------=_Part_6637_26146152.1128192797250
    Content-Type: text/plain; charset=ISO-8859-1
    Content-Transfer-Encoding: quoted-printable
    Content-Disposition: inline

    On 10/1/05, Trans <> wrote:
    >
    > z =3D lambda [
    > share :x
    > ]



    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?

    ------=_Part_6637_26146152.1128192797250--
    Linus Sellberg, Oct 1, 2005
    #3
  4. On Oct 1, 2005, at 12:20 PM, Eric Mahurin wrote:


    > 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



    >
    > __________________________________________________
    > 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/>
    Bob Hutchison, Oct 1, 2005
    #4
  5. Eric Mahurin

    Eric Mahurin Guest

    --- Trans <> wrote:

    > And a way to share vars from the above scope specifically:
    >=20
    > z =3D 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 =3D 1+2
    z =3D lambda {{
    # becomes equivalent to y =3D (x=3D3) when compiled
    y =3D 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).=20
    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 ;-)


    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.



    =09
    __________________________________=20
    Yahoo! Mail - PC Magazine Editors' Choice 2005=20
    http://mail.yahoo.com
    Eric Mahurin, Oct 1, 2005
    #5
  6. Eric Mahurin

    ES Guest

    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!
    ES, Oct 1, 2005
    #6
  7. Eric Mahurin

    Trans Guest

    Eric Mahurin wrote:
    > --- Trans <> 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 ;-)

    >
    > 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, Oct 1, 2005
    #7
  8. Eric Mahurin

    Eric Mahurin Guest

    --- ES <> 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).
    > >=20
    > > 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.
    > >=20
    > > 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.
    >=20
    > 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 =3D lambda { |a,b| "(#{a}+#{b})" }
    # will have to re-evaluate a or b if they are an expression
    min1 =3D lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
    # use local variables to prevent re-evaluation
    min2 =3D lambda { |a,b| "(a=3D#{a};b=3D#{b};a<b ? a : b)" }

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

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

    # need to localize a/b for the inner min2
    min2["x",min2["y","z"]]
    # =3D> "(a=3Dx;b=3D(a=3Dy;b=3Dz;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.



    =09
    __________________________________=20
    Yahoo! Mail - PC Magazine Editors' Choice 2005=20
    http://mail.yahoo.com
    Eric Mahurin, Oct 1, 2005
    #8
  9. On Oct 1, 2005, at 4:55 PM, Eric Mahurin wrote:


    > --- ES <> 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.
    >
    >


    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)

    ----
    Bob Hutchison -- blogs at <http://www.recursive.ca/hutch/>
    Recursive Design Inc. -- <http://www.recursive.ca/>
    Raconteur -- <http://www.raconteur.info/>
    Bob Hutchison, Oct 1, 2005
    #9
  10. Eric Mahurin

    ES Guest

    Eric Mahurin wrote:
    > --- ES <> 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, Oct 1, 2005
    #10
  11. Eric Mahurin

    Eric Mahurin Guest

    --- ES <> wrote:

    > Eric Mahurin wrote:
    > > --- ES <> wrote:
    > >=20
    > >=20
    > >>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).

    > >=20
    > >=20
    > > 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.
    > >=20
    > > plus =3D lambda { |a,b| "(#{a}+#{b})" }
    > > # will have to re-evaluate a or b if they are an expression
    > > min1 =3D lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
    > > # use local variables to prevent re-evaluation
    > > min2 =3D lambda { |a,b| "(a=3D#{a};b=3D#{b};a<b ? a : b)" }
    > >=20
    > > # y+z may get evaluated twice
    > > min1["x",plus["y","z"]]
    > > # =3D> "(x<(y+z) ? x : (y+z))"
    > >=20
    > > # y+z may evaluated once
    > > min2["x",plus["y","z"]]
    > > # =3D> "(a=3Dx;b=3D(y+z);a<b ? a : b)"
    > >=20
    > > # need to localize a/b for the inner min2
    > > min2["x",min2["y","z"]]
    > > # =3D> "(a=3Dx;b=3D(a=3Dy;b=3Dz;a<b ? a : b);a<b ? a : b)"
    > >=20
    > >=20
    > > See the problem on this last example? We really need to
    > > localize a and b. There isn't a good facility to do this.

    >=20
    > 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,
    >=20
    > foo =3D 5
    > bar =3D lambda {|foo| foo +=3D 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 =3D lambda { |a,b| "module;a=3D#{a};b=3D#{b};a<b ? a : b;end" }

    min2["x",min2["y","z"]]
    # =3D> "module;a=3Dx;b=3Dmodule;a=3Dy;b=3Dz;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.



    =09
    __________________________________=20
    Yahoo! Mail - PC Magazine Editors' Choice 2005=20
    http://mail.yahoo.com
    Eric Mahurin, Oct 2, 2005
    #11
  12. Eric Mahurin

    Eric Mahurin Guest

    --- Bob Hutchison <> 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 =3D lambda { |a,b| "(#{a}+#{b})" }
    > > # will have to re-evaluate a or b if they are an expression
    > > min1 =3D lambda { |a,b| "(#{a}<#{b} ? #{a} : #{b})" }
    > > # use local variables to prevent re-evaluation
    > > min2 =3D lambda { |a,b| "(a=3D#{a};b=3D#{b};a<b ? a : b)" }
    > >
    > > # y+z may get evaluated twice
    > > min1["x",plus["y","z"]]
    > > # =3D> "(x<(y+z) ? x : (y+z))"
    > >
    > > # y+z may evaluated once
    > > min2["x",plus["y","z"]]
    > > # =3D> "(a=3Dx;b=3D(y+z);a<b ? a : b)"
    > >
    > > # need to localize a/b for the inner min2
    > > min2["x",min2["y","z"]]
    > > # =3D> "(a=3Dx;b=3D(a=3Dy;b=3Dz;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.
    > >
    > >

    >=20
    > You need gensym as lisp has (and I had in a previous
    > example), in =20
    > which case you get this back on the last example, and there
    > is no =20
    > problem:
    >=20
    > (a_1=3Dx;b_2=3D(a_3=3Dy;b_4=3Dz;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 =3D lambda { |a,b|
    v1 =3D Gensym.gensym
    v2 =3D Gensym.gensym
    "(#{v1}=3D#{a};#{v2}=3D#{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




    =09
    __________________________________=20
    Yahoo! Mail - PC Magazine Editors' Choice 2005=20
    http://mail.yahoo.com
    Eric Mahurin, Oct 2, 2005
    #12
  13. On Oct 1, 2005, at 7:37 PM, Eric Mahurin wrote:



    > --- Bob Hutchison <> 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)
    >>
    >>
    >>

    >
    > 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

    ----
    Bob Hutchison -- blogs at <http://www.recursive.ca/hutch/>
    Recursive Design Inc. -- <http://www.recursive.ca/>
    Raconteur -- <http://www.raconteur.info/>
    Bob Hutchison, Oct 2, 2005
    #13
  14. Eric Mahurin

    ES Guest

    Eric Mahurin wrote:
    > --- ES <> wrote:
    >
    >
    >>Eric Mahurin wrote:
    >>
    >>>--- ES <> 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
    --
    No-one expects the Solaris POSIX implementation!
    ES, Oct 2, 2005
    #14
  15. Eric Mahurin

    Trans Guest

    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.
    Trans, Oct 2, 2005
    #15
  16. Eric Mahurin

    Eric Mahurin Guest

    --- Trans <> wrote:

    >=20
    > Eric Mahurin wrote:
    >=20
    > > 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.

    >=20
    > 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.=20
    module/end seemed to be a pretty close fit because module/end
    already localizes variables and executes code immediately.=20
    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.

    >=20
    > {{ }} 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.


    __________________________________________________
    Do You Yahoo!?
    Tired of spam? Yahoo! Mail has the best spam protection around=20
    http://mail.yahoo.com=20
    Eric Mahurin, Oct 2, 2005
    #16
  17. Eric Mahurin

    Eric Mahurin Guest

    --- Bob Hutchison <> wrote:
    > >> (a_1=3Dx;b_2=3D(a_3=3Dy;b_4=3Dz;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 =3D lambda { |a,b|
    > > v1 =3D Gensym.gensym
    > > v2 =3D Gensym.gensym
    > > "(#{v1}=3D#{a};#{v2}=3D#{b};#{v1}<#{v2} ? #{v1} : #{v2})"
    > > }
    > >
    > > Here are the disadvantages of this compared to having ruby

    > do
    > > local variables:
    > >
    > > - not as natural/uglier

    >=20
    > No argument there at all, I agree. However a little syntactic
    > sugar =20
    > would make all the difference. I made a suggestion off the
    > top of my =20
    > head as to what that might look like, but I've got no doubt
    > that that =20
    > can be improved.
    >=20
    > You might be able to make an argument that local variables
    > are more =20
    > fundamental and that a macro system shouldn't be the way to
    > solve the =20
    > problem. Though, the problem that motivated this discussion,
    > which I =20
    > believe amounted to a problem with in-lining functions, is
    > probably =20
    > 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

    >=20
    > Actually it doesn't in the current ruby. In the example I
    > gave =20
    > 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 =3D 1,-2,3
    # =3D> [1, -2, 3]
    local_variables
    # =3D> ["_", "x", "y", "z"]
    eval "(a_1=3Dx;b_2=3D(a_3=3Dy;b_4=3Dz;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

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


    Looks like they do.



    =09
    __________________________________=20
    Yahoo! Mail - PC Magazine Editors' Choice 2005=20
    http://mail.yahoo.com
    Eric Mahurin, Oct 2, 2005
    #17
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    0
    Views:
    563
  2. wallge
    Replies:
    14
    Views:
    716
    wallge
    Jan 30, 2007
  3. Daniel Berger

    Regexp#options (RCR #43)

    Daniel Berger, Jun 30, 2003, in forum: Ruby
    Replies:
    2
    Views:
    247
  4. Chris Angelico
    Replies:
    27
    Views:
    286
    Mark Lawrence
    Mar 5, 2013
  5. Gene Heskett
    Replies:
    0
    Views:
    104
    Gene Heskett
    Mar 5, 2013
Loading...

Share This Page