Why no Proc##[]=() ? Why no Proc##replace() ?

Discussion in 'Ruby' started by Jean-Hugues ROBERT, May 1, 2004.

  1. Hi,

    Looking at lvalues in Ruby I learned a lot. I thought Proc
    instances could be turned into lvalues too, so I tried:

    class Proc
    def []=(*args) self[*args] end
    end

    pp = proc { |*x| @val = x[0] unless x.empty? ; @val }
    pp[] = 1 # => 1
    p pp[] # => 1

    Ruby's cool, no doubts.
    Is this worth a RCR ?

    Then I tried to rebuilt my version of a Pointer class at
    http://www.c2.com/cgi/wiki?SinisterSchemeSampleInRuby
    with what I had learnt.

    class Pointer < Proc
    def initialize( &block ) ref( &block) end
    def ref( &block )
    lvalue = block.call()
    tmp = eval( "proc { |*x| #{lvalue} = x[0] unless x.empty? ; #{lvalue}
    }", block)
    self.replace tmp
    end
    def to_s() self[].to_s() end
    def inspect() "<Pointer>#{self[].inspect()}" end
    end

    That would be considerably simpler, the body makes 8 lines versus 16.
    But it does not work, there is no Proc##replace()

    Yours,

    Jean-Hugues





    ------------------------------------------------------------------------
    Web: http://hdl.handle.net/1030.37/1.1
    Phone: +33 (0) 4 92 27 74 17
    Jean-Hugues ROBERT, May 1, 2004
    #1
    1. Advertising

  2. Jean-Hugues ROBERT

    Dan Doel Guest

    I'm not sure what your proposal means.

    If x is a Proc object, then

    x[arg1, arg2, ...]

    Means to call the code stored by the Proc with the specified arguments.

    What would

    x[arg1, arg2, ...] = val1, ...

    Mean? In your example, we don't appear to be doing any actual assigning.
    We're just moving an argument to the proc out of the []. So the following
    would be equivalent:

    x[a, b, c]
    x[a, b] = c

    I'm not sure why you would want to do that. Also, Proc objects are simply
    wrappers around some code, so I see only two ways that x[*args] = b would
    make any sense semantically:

    1) x[*args] returns some assignable value. But Ruby doesn't allow overriding
    of assignment, so that point is moot.
    2) x[*args] = c makes x contain new code somehow. But that doesn't make any
    sense either, really.

    Also, what would Proc#replace do? Replace the code to be executed? You can
    just make a new Proc if you want to do that. The only reason that #replace
    could be different is if it kept the original binding, which means that you
    could make a proc in one scope that creates variables in a completely
    different scope, which would probably be impossible to compile, among other
    things.

    Could you explain some more?

    - Dan
    Dan Doel, May 1, 2004
    #2
    1. Advertising

  3. About class Proc; def []=(*args); self[*args] end end and the value of
    replace()

    At 06:43 02/05/2004 +0900, you wrote:
    >I'm not sure what your proposal means.
    >If x is a Proc object, then
    > x[arg1, arg2, ...]
    >Means to call the code stored by the Proc with the specified arguments.
    >What would
    > x[arg1, arg2, ...] = val1, ...
    >Mean? In your example, we don't appear to be doing any actual assigning.
    >We're just moving an argument to the proc out of the []. So the following
    >would be equivalent:
    > x[a, b, c]
    > x[a, b] = c

    That is right. Then the block can act as a rvalue or a lvalue depending
    on how many parameters it receives.

    >I'm not sure why you would want to do that. Also, Proc objects are simply
    >wrappers around some code, so I see only two ways that x[*args] = b would
    >make any sense semantically:
    > 1) x[*args] returns some assignable value. But Ruby doesn't allow
    > overriding
    > of assignment, so that point is moot.
    > 2) x[*args] = c makes x contain new code somehow. But that doesn't make any
    > sense either, really.

    I agree that the notion of lvalue is not that obvious. A lvalue is simply
    something that can stand on the left side of an assignment =.
    lvalue = rvalue.
    That a variable can be a lvalue is rather obvious:
    l = x, @i = x, @@c = x, $G = x
    But a method call can be a lvalue too.
    a = [1,2,3] # lvalue is variable a
    a[3] = 4 # lvalue is a[3], Ruby turns that into a.[]=( 4).
    o.i = 4 # lvalue is o.i, Ruby turns that into a.i=( 4)
    What append if a is a Proc instead of an Array
    a = proc { |*args| p args }
    a[3] = 4 # Turned into a.[]=( 4)
    Now, because Proc's method []=(*args) is defined as self[*args],
    it is a[4] that is executed. Which is turned in a.call( 4).
    As a result, args in the proc becomes [4].
    p a[] # Turned in a.[]()
    This is now the rvalue case, args in the proc will be [].
    So: depending on the length of args, the proc can determine
    the intend of the caller: please act as a rvalue, or please act as a lvalue.
    Of course, the caller can p a[4]. The intent is ambiguous.

    >Also, what would Proc#replace do? Replace the code to be executed? You can
    >just make a new Proc if you want to do that. The only reason that #replace
    >could be different is if it kept the original binding, which means that you
    >could make a proc in one scope that creates variables in a completely
    >different scope, which would probably be impossible to compile, among other
    >things.


    You are half right; it is true that the original binding matters but
    a "replaced" proc is not a new Proc, it's the same object, redefined.

    I would think that Proc#replace would do something similar to other
    implementation of replace: change the value of the object in place.

    a = "aaa"
    p a.object_id()
    a.replace "bbb"
    p a.object_id() # a still reference the same object
    a = "ccc"
    p a.object_id() # a references a new object

    Now, what is Proc#replace() useful for ?

    You can imagine multiple cases where the Proc itself wants to change its
    own definition. For example, imagine a Proc that caches its result:
    r = proc {
    x = long_method
    self.replace { x }
    x
    }
    r.call() # long_method called
    r.call() # cached result

    There are other ways to do this of course, without replace:
    p = proc { @x ||= long_method }
    or (needed if result can be nil)
    p = proc { @x = long_method unless defined? @x; @x }

    Now, which method is more efficient ?

    >Could you explain some more?


    Sure, just ask.

    >- Dan


    Yours,

    Jean-Hugues


    -------------------------------------------------------------------------
    Web: http://hdl.handle.net/1030.37/1.1
    Phone: +33 (0) 4 92 27 74 17
    Jean-Hugues ROBERT, May 2, 2004
    #3
  4. On Sun, May 02, 2004 at 05:18:09PM +0900, Jean-Hugues ROBERT wrote:
    > There are other ways to do this of course, without replace:
    > p = proc { @x ||= long_method }
    > or (needed if result can be nil)
    > p = proc { @x = long_method unless defined? @x; @x }


    It seems to me that you're assuming @x is an instance variable of the
    Proc object, which it is not:

    def foo; 1 end
    # =>nil
    p = proc { @x ||= foo }
    # =>#<Proc:0x401f9cec@(irb):2>
    @x
    # => (irb):3: warning: instance variable @x not initialized
    # =>nil
    p[]
    # =>1
    @x
    # =>1


    --
    Running Debian GNU/Linux Sid (unstable)
    batsman dot geo at yahoo dot com

    'Ooohh.. "FreeBSD is faster over loopback, when compared to Linux
    over the wire". Film at 11.'
    -- Linus Torvalds
    Mauricio Fernández, May 2, 2004
    #4
  5. Jean-Hugues ROBERT

    Dan Doel Guest

    I understand the concept of an lvalue in assignment. I just don't think it
    really makes sense for proc calling.

    Method calls in Ruby can be lvalues, yes. However, they are used as idioms
    for object oriented programming. For example:

    a.foo = b

    Is used to access the foo attribute of b. That's the only way you'll have the
    appearance of public variables in Ruby, and otherwise you'll have to relegate
    yourself to

    a.set_foo(b)

    which is less pretty. []= is usually used for objects that have multiple
    accessible elements based on some index, like arrays and hashes. However,
    Proc objects don't have such indexable values. [] is an idiom for calling the
    Proc. Procs are like functions in Ruby, and []= would be like assigning to
    the result of the function, which is sort of like doing something like:

    1 = a

    At least to my mind. It feels like treating assignment as an operation on
    objects, rather than an operation on variables (and it's the latter, not the
    former in Ruby). Would you also argue that

    p = lambda {...}
    p.call = a, b, c

    should be well defined?

    > Now, what is Proc#replace() useful for ?
    >
    > You can imagine multiple cases where the Proc itself wants to change its
    > own definition. For example, imagine a Proc that caches its result:
    > r = proc {
    > x = long_method
    > self.replace { x }
    > x
    > }
    > r.call() # long_method called
    > r.call() # cached result
    >
    > There are other ways to do this of course, without replace:
    > p = proc { @x ||= long_method }
    > or (needed if result can be nil)
    > p = proc { @x = long_method unless defined? @x; @x }


    These two work, and they don't use instance variables (which, as someone else
    pointed out, aren't actually the instance variables of the Proc):

    x = nil
    p = lambda { x ||= long_method }

    b = true
    r = lambda { if b then b = false; x = long_method else x end }

    And I don't see why this wouldn't work in this situation:

    x = long_method
    p = lambda { x }

    > Now, which method is more efficient ?


    The replace method might be more efficient if you call the Proc many times,
    but in the short term, I don't think either one will be wildly more efficient
    than the other. In fact, I'd wager that modifying the object would require
    more resources than doing a few boolean tests.

    Also, incidentally, in your example it'd have to be r.replace { x }, as self
    in the block is not the Proc.

    I guess self-modifying code could be a good thing, but it seems like overkill
    for caching the result of a function. :)

    Anyhow, that's all I can think of to say at the moment, so I'll cut this off.

    Cheers.

    - Dan
    Dan Doel, May 2, 2004
    #5
  6. At 22:10 02/05/2004 +0900, you wrote:
    >On Sun, May 02, 2004 at 05:18:09PM +0900, Jean-Hugues ROBERT wrote:
    > > There are other ways to do this of course, without replace:
    > > p = proc { @x ||= long_method }
    > > or (needed if result can be nil)
    > > p = proc { @x = long_method unless defined? @x; @x }

    >
    >It seems to me that you're assuming @x is an instance variable of the
    >Proc object, which it is not:


    You are right, I was wrong. I believed @x was an instance variable
    of Proc object.

    >def foo; 1 end
    ># =>nil
    >p = proc { @x ||= foo }
    ># =>#<Proc:0x401f9cec@(irb):2>
    >@x
    ># => (irb):3: warning: instance variable @x not initialized
    ># =>nil
    >p[]
    ># =>1
    >@x
    ># =>1


    Hum... OK. I am wrong. How can I have a block variable that survives
    multiple across multiple invocation ?

    I had tried with a local variable, but that did not work. So I tried
    with an instance variable, and it seemed to work (but does not,
    because I was wrong assuming self was the Proc object, whereas in fact
    it is the enclosing method's self, which makes sense BTW).

    BTW: Where can I save the result of long_method, if neither in a
    class variable, nor in a local variable ?

    Is there such a thing that "current_block()" or whatever method
    that tells me what the Proc object is ?

    I guess my example with self.replace { mutated_block } was wrong too
    (it was not supposed to work anyways because replace is not there
    for Proc/s). So... How can a block remember things ?

    Yours,

    Jean-Hugues

    >--
    >Running Debian GNU/Linux Sid (unstable)
    >batsman dot geo at yahoo dot com
    >
    >'Ooohh.. "FreeBSD is faster over loopback, when compared to Linux
    >over the wire". Film at 11.'
    > -- Linus Torvalds


    -------------------------------------------------------------------------
    Web: http://hdl.handle.net/1030.37/1.1
    Phone: +33 (0) 4 92 27 74 17
    Jean-Hugues ROBERT, May 3, 2004
    #6
  7. Jean-Hugues ROBERT

    Dan Doel Guest

    Local variables work, but they have to be defined outside of the scope of the
    block:

    x = nil
    p = lambda { x ||= long_method }

    p.call
    p.call

    This only calls long_method once, at least on my version of Ruby (1.8.1).

    Apparently, without the preceding "x = nil" x goes back to being undefined
    when the block closes, so changes to it aren't persistant. If it's defined
    outside the scope of the block, though, those changes stay.

    Maybe this will change when locals leak out of block scoping. Currently if
    you want x to be accessible after the block, you need the preceding x = nil.
    But in 2.0 it's changing so that you won't, so maybe you won't need it in
    this case either.

    - Dan
    Dan Doel, May 3, 2004
    #7
  8. Jean-Hugues ROBERT

    Zsban Ambrus Guest

    On Mon, May 03, 2004 at 01:45:45PM +0900, Jean-Hugues ROBERT wrote:
    >
    > Hum... OK. I am wrong. How can I have a block variable that survives
    > multiple across multiple invocation ?
    >
    > I had tried with a local variable, but that did not work. So I tried
    > with an instance variable, and it seemed to work (but does not,
    > because I was wrong assuming self was the Proc object, whereas in fact
    > it is the enclosing method's self, which makes sense BTW).
    >
    > BTW: Where can I save the result of long_method, if neither in a
    > class variable, nor in a local variable ?


    You don't. A proc is just a simple lambda without mutable internal state.
    If you want a proc-like object that can have mutable internal state,
    define a class and define its [] method. That's simpler.

    Ok, it is possible with lambdas too, but I'm not sure how exactly.

    >
    > Is there such a thing that "current_block()" or whatever method
    > that tells me what the Proc object is ?
    >
    > I guess my example with self.replace { mutated_block } was wrong too
    > (it was not supposed to work anyways because replace is not there
    > for Proc/s). So... How can a block remember things ?
    >
    > Yours,
    >
    > Jean-Hugues
    >
    Zsban Ambrus, May 3, 2004
    #8
  9. Thanks.
    I guess I need to :
    while not done_xxx
    begin
    x = nil
    p = lambda { x ||= long_method }
    end
    ...
    using begin/end to make sure that *each* proc has its own x
    EOM
    Jean-Hugues

    At 14:39 03/05/2004 +0900, you wrote:
    >Local variables work, but they have to be defined outside of the scope of the
    >block:
    >
    > x = nil
    > p = lambda { x ||= long_method }
    >
    > p.call
    > p.call
    >
    >This only calls long_method once, at least on my version of Ruby (1.8.1).
    >
    >Apparently, without the preceding "x = nil" x goes back to being undefined
    >when the block closes, so changes to it aren't persistant. If it's defined
    >outside the scope of the block, though, those changes stay.
    >
    >Maybe this will change when locals leak out of block scoping. Currently if
    >you want x to be accessible after the block, you need the preceding x = nil.
    >But in 2.0 it's changing so that you won't, so maybe you won't need it in
    >this case either.
    >
    >- Dan


    -------------------------------------------------------------------------
    Web: http://hdl.handle.net/1030.37/1.1
    Phone: +33 (0) 4 92 27 74 17
    Jean-Hugues ROBERT, May 3, 2004
    #9
  10. About hypothetical class Proc; def []=(*args) self[*args] end end

    At 07:21 03/05/2004 +0900, you wrote:
    >I understand the concept of an lvalue in assignment. I just don't think it
    >really makes sense for proc calling.


    Sometimes, a Proc is sometimes like an anonymous method, right ?
    If assignment does not make sense for proc p,
    then is does not make sense for method m.
    then m.[]=( 1, x) does not make any more sense than p.[]=( 1, x)
    Then m[1] = x does not make sense either
    Then it is an embarrassing situation because a[1] = x does not make sense...
    So, it has to make sense for method, and it should make sense for proc too.
    Am I missing something ?

    >[]= is usually used for objects that have multiple
    >accessible elements based on some index, like arrays and hashes. However,
    >Proc objects don't have such indexable values. [] is an idiom for calling the
    >Proc. Procs are like functions in Ruby, and []= would be like assigning to
    >the result of the function, which is sort of like doing something like:
    > 1 = a
    >At least to my mind. It feels like treating assignment as an operation on
    >objects, rather than an operation on variables (and it's the latter, not the
    >former in Ruby).


    That is true, it feels like treating assignment as an operation on object.
    But it not true that Ruby treats assignment as an operation on variables,
    it does so only for scalar object. For non scalars, arrays, hash, etc,
    assignment is by method call (.[]=() on the non scalar object).

    This is not symetric. Ruby could always treat assignment as an operation
    on an object. This requires: a Lvalue class and the ability to redefine
    the assignment operator. As a result a variable could easily be an instance
    of Lvalue and we would get closer to the motto "Everything is an object in
    Ruby". I am currently prototyping such a Lvalue class.

    For speed reason, the actual Lvalue instance could be created only when
    the user needs it, say: lvalue = ref a.

    > Would you also argue that
    > p = lambda {...}
    > p.call = a, b, c should be well defined?


    If there was a Lvalue class, and if p was to return a lvalue, then I guess
    p.call = a, b, c
    would be equivalent to
    lvalue = a, b, c
    which is equivalent to
    lvalue = a.
    I would then expect the lvalue to be assigned a. Either by calling lvalue.=(a)
    or directly by the interpretor.

    But there is no Lvalue class today. The closer is a user defined Pointer class:
    p = lambda { ... return an instance of Pointer }
    p.call()[]= a, b, c
    eqv p.call().[]=(a)

    As you can see, with a Pointer, you have to dereference yourself using [],
    whereas with a Lvalue you would not.

    > > Now, what is Proc#replace() useful for ?
    > > You can imagine multiple cases where the Proc itself wants to change its
    > > own definition. For example, imagine a Proc that caches its result:
    > > r = proc {
    > > x = long_method
    > > self.replace { x }
    > > x
    > > }


    I made 2 mistakes, self is not the Proc object, x gets scoped out.
    r.replace { x } would work, but then r = proc { x } is more obvious.
    I don't know how to get the current Proc object from the proc's body.
    Any clue ?

    > > r.call() # long_method called
    > > r.call() # cached result
    > >
    > > There are other ways to do this of course, without replace:
    > > p = proc { @x ||= long_method }
    > > or (needed if result can be nil)
    > > p = proc { @x = long_method unless defined? @x; @x }


    Nota: @x is actually an instance variable of the object that
    defined the proc. I tought it was an instance variable of the
    Proc object itself, but I was wrong. Sorry about that.

    >These two work, and they don't use instance variables (which, as someone else
    >pointed out, aren't actually the instance variables of the Proc):
    >
    > x = nil
    > p = lambda { x ||= long_method }
    >
    > b = true
    > r = lambda { if b then b = false; x = long_method else x end }


    True. Now they use a local variable of the method instead of
    an instance variable of the object.

    >And I don't see why this wouldn't work in this situation:
    >
    > x = long_method
    > p = lambda { x }


    Well, in this case you call long_method even if the proc is never called.
    That was not the purpose of the example. The example is a cache where
    long_method is call at most once, but only if needed (cached & lazy !).

    > > Now, which method is more efficient ?

    >
    >The replace method might be more efficient if you call the Proc many times,
    >but in the short term, I don't think either one will be wildly more efficient
    >than the other. In fact, I'd wager that modifying the object would require
    >more resources than doing a few boolean tests.


    I suspect it makes sense that a value is cached precisely when it is
    often needed. With replace() the overhead of replace() decreases as
    you need the cached value more and more, at some point it becomes
    negligeable.

    >Also, incidentally, in your example it'd have to be r.replace { x }, as self
    >in the block is not the Proc.


    True. I still need to figure how to get the current proc object when
    executing the block's body.

    >I guess self-modifying code could be a good thing, but it seems like overkill
    >for caching the result of a function. :)


    That's a matter of style I guess. Besides, that was just a short convenient
    example
    (slightly broken) to show what Proc#replace() would do.

    >Anyhow, that's all I can think of to say at the moment, so I'll cut this off.


    Thanks for the feedback.

    >Cheers.
    >- Dan


    Yours,

    Jean-Hugues



    -------------------------------------------------------------------------
    Web: http://hdl.handle.net/1030.37/1.1
    Phone: +33 (0) 4 92 27 74 17
    Jean-Hugues ROBERT, May 3, 2004
    #10
  11. Jean-Hugues ROBERT

    Dan Doel Guest

    On Monday 03 May 2004 7:54 am, Jean-Hugues ROBERT wrote:
    > Sometimes, a Proc is sometimes like an anonymous method, right ?
    > If assignment does not make sense for proc p,
    > then is does not make sense for method m.
    > then m.[]=( 1, x) does not make any more sense than p.[]=( 1, x)
    > Then m[1] = x does not make sense either
    > Then it is an embarrassing situation because a[1] = x does not make
    > sense... So, it has to make sense for method, and it should make sense for
    > proc too. Am I missing something ?


    Right. If m is a Method object, then m[1] = x doesn't make sense either.
    m[1] = x only makes sense to me if m is an Array or a Hash or a String or
    something where it makes sense to talk about the Nth element of m. Proc and
    Method objects are not such types, in my opinion.

    > That is true, it feels like treating assignment as an operation on object.
    > But it not true that Ruby treats assignment as an operation on variables,
    > it does so only for scalar object. For non scalars, arrays, hash, etc,
    > assignment is by method call (.[]=() on the non scalar object).


    We look at this in different ways. Let me try to explain my point of view more
    clearly.

    o.a = b

    is to me a nicer way of saying

    o.set_a(b)

    Which is what you'd do in Java. In Java, you can have lvalues of the form o.a
    if a is a public variable o. This is _never_ the case in Ruby, because there
    are only private instance variables. However, Matz recognizes that it looks
    nicer if you can make attribute accessors look like public instance variables,
    so he wisely allowed for the syntax sugar to do so.

    If a is an Array, then:

    a = j

    means:

    a.add_at_index(i, j)

    And similarly for Hashes and Strings. However, the semantic for [] on Proc
    and Method objects is:

    a <==> a.call(i)

    However, I cannot think of any meaningful name for:

    a = j

    Which in your example turns into:

    a.call(i, j)

    Even stranger might be:

    a[i, j, k] = l, m, n #=> becomes a.call(i, j, k, [l, m, n])

    In other words, for Proc and Method, [] is taken to mean "execute encapsulated
    code." However, "execute encapsulated code equals" doesn't make sense to me,
    at least in the context of Ruby.

    > This is not symetric. Ruby could always treat assignment as an operation
    > on an object. This requires: a Lvalue class and the ability to redefine
    > the assignment operator. As a result a variable could easily be an instance
    > of Lvalue and we would get closer to the motto "Everything is an object in
    > Ruby". I am currently prototyping such a Lvalue class.


    I don't really see how this would work. It would require a fundamental change
    in the way variables work in Ruby. For example, what happens when you do:

    c = Foo.new

    If c is an Lvalue class, then the variable that c refers to becomes a new Foo
    object. Otherwise, c becomes a new instance of Foo. However, this means
    that all Ruby variables would have to be by-value instead of by-reference as
    they are now. For example, if you did:

    c = b

    Currently c and b refer to the same object. In order to make your changes
    work, this would not be possible. I think this is a step backwards. It makes
    programmers think about what kind of memory they want to use (heap or
    stack, by-value or by-reference?). Not to mention that by-reference would
    be by far more used, considering Ruby has essentially _no_ by value variables
    at present and it does just fine.

    This is all not to mention the fact that Ruby variables are untyped, so they
    need to be able to refer to amounts of memory to handle arbitrarily large
    objects. The above scheme would require something similar to #become in
    Smalltalk. The difference would be that for most objects, assignment would
    default to #become, rather than it being an uncommonly used method.

    > If there was a Lvalue class, and if p was to return a lvalue, then I guess
    > p.call = a, b, c
    > would be equivalent to
    > lvalue = a, b, c
    > which is equivalent to
    > lvalue = a.
    > I would then expect the lvalue to be assigned a. Either by calling
    > lvalue.=(a) or directly by the interpretor.
    >
    > But there is no Lvalue class today. The closer is a user defined Pointer
    > class: p = lambda { ... return an instance of Pointer }
    > p.call()[]= a, b, c
    > eqv p.call().[]=(a)
    >
    > As you can see, with a Pointer, you have to dereference yourself using [],
    > whereas with a Lvalue you would not.


    Based on what I said above, I think it's by far better to require someone to
    use a pointer class to make non-local variable assignments. The actual need
    for them doesn't come up particularly often, and the consequences of making
    assignment object-based are, if you ask me, bad.

    > I made 2 mistakes, self is not the Proc object, x gets scoped out.
    > r.replace { x } would work, but then r = proc { x } is more obvious.
    > I don't know how to get the current Proc object from the proc's body.
    > Any clue ?


    Well, in general, you'll have

    r = lambda { ... }

    The block captures the scope in which it resides, so you can use r in the
    block to refer to the Proc itself. That's the only way, as far as I know.

    > Well, in this case you call long_method even if the proc is never called.
    > That was not the purpose of the example. The example is a cache where
    > long_method is call at most once, but only if needed (cached & lazy !).


    True. My last example isn't lazy.

    > I suspect it makes sense that a value is cached precisely when it is
    > often needed. With replace() the overhead of replace() decreases as
    > you need the cached value more and more, at some point it becomes
    > negligeable.


    Note that this does in fact work:

    def foo
    puts "foo"
    6
    end

    r = lambda {
    x = foo
    r = lambda { x }
    x
    }

    p r.call
    p r.call
    p r.call

    This only calls #foo once, and properly caches the result. However, this is
    only because we're referring to r by that variable each time. Passing it to

    def bar(p)
    p.call
    p.call
    end

    Results in #foo being called twice. However, I'd still say that Proc objects
    shouldn't be mutable (in the traditional sense). That's merely my opinion,
    though.

    Cheers.

    - Dan
    Dan Doel, May 3, 2004
    #11
  12. At 19:06 03/05/2004 +0900, you wrote:
    >On Mon, May 03, 2004 at 01:45:45PM +0900, Jean-Hugues ROBERT wrote:
    > > > Hum... OK. I am wrong. How can I have a block variable that survives

    > > multiple across multiple invocation ?
    > >

    >You don't. A proc is just a simple lambda without mutable internal state.
    >If you want a proc-like object that can have mutable internal state,
    >define a class and define its [] method. That's simpler.


    OK. So:

    class CacheProc < Proc
    def [](*args) @val = call(*args) unless defined? @val; @val end
    end

    Now:
    x = CacheProc.new { long_method }
    p x[] # => result of long_method
    p x[] # => cached result
    p x.call() # result of long_method
    p x[] # => cached result (first one)

    Thanks !

    >Ok, it is possible with lambdas too, but I'm not sure how exactly.


    So far I am assuming that proc and lambda are the same, you seem
    unsure about that.

    Yours,

    Jean-Hugues
    Jean-Hugues ROBERT, May 3, 2004
    #12
  13. Warning: I believe this thread of msgs is rather academic. Because I
    initiated it I thought it might be useful to tell you that. if you don't
    enjoy academic stuff, save your time and skip this msg :)

    class Proc
    def []=(*a)
    self[*a]
    end
    end

    Some consequences:

    At 00:53 04/05/2004 +0900, you wrote:
    >On Monday 03 May 2004 7:54 am, Jean-Hugues ROBERT wrote:
    > > Sometimes, a Proc is sometimes like an anonymous method, right ?
    > > If assignment does not make sense for proc p,
    > > then is does not make sense for method m.
    > > then m.[]=( 1, x) does not make any more sense than p.[]=( 1, x)
    > > Then m[1] = x does not make sense either
    > > Then it is an embarrassing situation because a[1] = x does not make
    > > sense... So, it has to make sense for method, and it should make sense for
    > > proc too. Am I missing something ?

    >
    >Right. If m is a Method object, then m[1] = x doesn't make sense either.


    OK. To summarize: I feel it makes sense that: class Method; def []=(*a)
    self[*a] end end
    And you feel like it does not make sense (if I understand correctly).

    Lets try (using Proc instead of Method, we both agree I think that a Proc
    can act as an anonymous method).

    # Make it so that a Proc can be a valid lvalue:
    class Proc; def []=(*a) self[*a] end end

    def trace_array( an_array, msg )
    proc { |*args|
    if args.length() == 1 then
    p "#{msg}: Read access at pos #{args[0]}"
    an_array[args[0]]
    elsif args.length() == 2 then
    p "#{msg}: Write access at pos #{args[0}"
    an_array[args[0]] = args[1]
    else
    p "#{msg}: Weird access."
    an_array[*args[0...-1]] = args[-1]
    end
    }
    end

    def buggy_meth()
    a = [1,2,3]
    a = trace_array( a, "a in buggy_meth()") if $Debug
    ... use a ...
    end

    In this example you can substitute a Proc where an Array was expected.
    The Proc is invoked both for read and write accesses to the Array.
    It outputs a msg and then performs the required operation on the Array.

    This is a simple debugging tool made possible thanks to an additional
    level of indirection.

    With a Lvalue class that I am going to propose soon in a RCR, one
    could do something similar on all lvalues, not just the [] ones:

    def trace_lvalue( a_lvalue, msg )
    proc { |*args|
    if args.length() == 0 then
    p "#{msg}: Read access"
    a_lvalue
    else
    p "#{msg}: Write access"
    a_lvalue = args[0]
    end
    end
    end

    def buggy_meth()
    a = false
    a = trace_lvalue( ref a, "a flag in buggy_meth()") if $Debug
    ... Use a ...
    end

    In this example, you can substitute a Proc wherever a lvalue was
    expected. The Proc is invoked both for read and write accesses to
    the lvalue.

    This is probably meta-programing, but I feel like I often need help
    when I am chasing bugs (I make a lot of them ;-))

    >m[1] = x only makes sense to me if m is an Array or a Hash or a String or
    >something where it makes sense to talk about the Nth element of m. Proc and
    >Method objects are not such types, in my opinion.


    Fair enough.

    > > That is true, it feels like treating assignment as an operation on object.
    > > But it not true that Ruby treats assignment as an operation on variables,
    > > it does so only for scalar object. For non scalars, arrays, hash, etc,
    > > assignment is by method call (.[]=() on the non scalar object).

    >
    >We look at this in different ways. Let me try to explain my point of view more
    >clearly.
    >
    > o.a = b
    >
    >is to me a nicer way of saying
    >
    > o.set_a(b)
    >
    >Which is what you'd do in Java. In Java, you can have lvalues of the form o.a
    >if a is a public variable o. This is _never_ the case in Ruby, because there
    >are only private instance variables. However, Matz recognizes that it looks
    >nicer if you can make attribute accessors look like public instance variables,
    >so he wisely allowed for the syntax sugar to do so.
    >
    >If a is an Array, then:
    >
    > a = j
    >
    >means:
    >
    > a.add_at_index(i, j)
    >
    >And similarly for Hashes and Strings. However, the semantic for [] on Proc
    >and Method objects is:
    >
    > a <==> a.call(i)
    >
    >However, I cannot think of any meaningful name for:
    >
    > a = j
    >Which in your example turns into:
    >
    > a.call(i, j)
    >
    >Even stranger might be:
    >
    > a[i, j, k] = l, m, n #=> becomes a.call(i, j, k, [l, m, n])


    Its actually # => becomes a.call( i, j, k, l).

    >In other words, for Proc and Method, [] is taken to mean "execute encapsulated
    >code." However, "execute encapsulated code equals" doesn't make sense to me,
    >at least in the context of Ruby.


    In Java you implement accessors using .getX() and .setX().
    class MyClass
    def getX() @x end
    def setX() @x = v end
    end
    In Ruby the "standard" is to use x() and x=().
    class MyClass
    def x() @x end
    def x=( v) @x = v end
    end
    It is so frequent that you have a convenience method that does it for you
    class MyClass
    attr_accessor :x # defines both x() and x=()
    end
    Another "style" of accessors is o.x() & o.x(y):
    class MyClass
    def x(*a) (a.length() == 0) ? @x : (@x = a[0]) # x() is both a getter
    and a setter
    end

    We both agree I think that o.x = y is a nicer syntax then o.x( y).
    Only I think that p[] = y is a nicer syntax then p[y] if proc were an accessor.

    Another one:
    If ptr = Pointer.new(...) then p[] = x is nicer syntax (to me) than p.set(
    x) because
    to me it reads "the content of p is assigned the value of x".

    Not to mention: x = y versus x.set( y).

    Back to block: b[] = x, to me, reads as "the content of b is assigned the
    value of x".
    What that means exactly depends on the semantic of the block b.

    Back to assignment:
    class SomeClass
    def self.factory( *args )
    if xxx
    proc { |*a| ... }
    elsif yyy
    method( :something)
    elsif zzz
    ConcreteClassA.new( *args)
    else
    OtherConcreteClass.new( *args)
    end
    end
    end
    something = SomeClass.factory( xxx)
    p something[0]
    something[0] = 1

    As a user of class SomeClass, I don't need to care about how accessors are
    implemented.
    This is both encapsulation and polymorphing.

    My conclusion is:
    When x[ii] means "content of x" and x[ii]= y means "content of x is
    assigned the value of y",
    it makes sense that x can be anything, a Method or a Block included,
    because I should
    not care about that, it is up to the implementation to decide.
    As a consequence it makes sense to define Proc##[]= as much as Proc##[] and
    def []=(*a) self[*a] end makes sense as a default implementation.

    OTOH the whole issue may be academic, because as a user, one can already:
    class ProcPointer < Proc
    def []=(*a)
    self[*a]
    end
    end
    Which I believe is cleaner than
    class Proc
    def []=(*a)
    self[*a]
    end
    end
    because the later one can be easily overlooked and as a result decreases the
    readability of the code.
    Yet, if the later one was included in Ruby, I believe the Proc class would
    be more versatile.

    > > This is not symetric. Ruby could always treat assignment as an operation
    > > on an object. This requires: a Lvalue class and the ability to redefine
    > > the assignment operator. As a result a variable could easily be an instance
    > > of Lvalue and we would get closer to the motto "Everything is an object in
    > > Ruby". I am currently prototyping such a Lvalue class.

    >
    >I don't really see how this would work. It would require a fundamental change
    >in the way variables work in Ruby. For example, what happens when you do:
    >
    > c = Foo.new
    >
    >If c is an Lvalue class, then the variable that c refers to becomes a new Foo
    >object. Otherwise, c becomes a new instance of Foo. However, this means
    >that all Ruby variables would have to be by-value instead of by-reference as
    >they are now.


    If c is_a? Lvalue then the lvalue object that c refers to (& which can be
    any lvalue,
    a variable included), becomes a new Foo (which means that it now refers to
    a Foo:
    c now refers to the same something but that something now refers to the new
    Foo).

    However, this does not mean that all Ruby variables would have to be different
    from what they are today (whatever the name you use to describe what they are
    today). Only variables that holds a reference to a Lvalue object would have
    to be treated differently than the "normal" variables. That's because the Ruby
    interpretor would have to invoke some .getter() or .setter() method of the
    Lvalue instead of using the variable's content directly (or, to rephrase more
    formally, xxx instead of directly using the reference to some object that the
    variable holds).

    > For example, if you did:
    >
    > c = b
    >
    >Currently c and b refer to the same object. In order to make your changes
    >work, this would not be possible. I think this is a step backwards. It makes
    >programmers think about what kind of memory they want to use (heap or
    >stack, by-value or by-reference?). Not to mention that by-reference would
    >be by far more used, considering Ruby has essentially _no_ by value variables
    >at present and it does just fine.


    I am not proposing such a radical change at all. I would rather go forward
    than backward :) What I am proposing is an additional tool, by the way of an
    additional level of indirection. When the programmer need that tool it has
    to be explicit and she/he would create a Lvalue object using some explicit
    syntax:
    b = "toto"
    c = ref b # *explicit*
    c = "titi"
    p b # => "titi"
    c is like an alias for b.

    >This is all not to mention the fact that Ruby variables are untyped, so they
    >need to be able to refer to amounts of memory to handle arbitrarily large
    >objects. The above scheme would require something similar to #become in
    >Smalltalk. The difference would be that for most objects, assignment would
    >default to #become, rather than it being an uncommonly used method.


    #become is a nice tool. But its use and abuse is programmer's responsability.

    The implementation of Lvalue that I am propotyping does not use #become
    (#become BTW is not yet fully available I believe, but that is not the reason).

    b = "toto"
    p b.object_id() # 123
    c = ref b
    c = "titi"
    p b.object_id() # 456
    If I were to use #become, b.object_id() would stay the same. It's not the
    case and must not be.

    As a matter of fact, I am very unsure that a Lvalue class could be implemented
    at all using #become.

    > > If there was a Lvalue class, and if p was to return a lvalue, then I guess
    > > p.call = a, b, c
    > > would be equivalent to
    > > lvalue = a, b, c
    > > which is equivalent to
    > > lvalue = a.
    > > I would then expect the lvalue to be assigned a. Either by calling
    > > lvalue.=(a) or directly by the interpretor.
    > >
    > > But there is no Lvalue class today. The closer is a user defined Pointer
    > > class: p = lambda { ... return an instance of Pointer }
    > > p.call()[]= a, b, c
    > > eqv p.call().[]=(a)
    > >
    > > As you can see, with a Pointer, you have to dereference yourself using [],
    > > whereas with a Lvalue you would not.

    >
    >Based on what I said above, I think it's by far better to require someone to
    >use a pointer class to make non-local variable assignments. The actual need
    >for them doesn't come up particularly often, and the consequences of making
    >assignment object-based are, if you ask me, bad.


    Then you don't mind that much that "In Ruby everything is an object, but
    variables and
    ... and ...".
    I would prefer "In Ruby everything is an object". Introspection is a great
    tool, the more, the better.

    But goods and bads is all relative and its OK that our opinions
    differs... My "match, assign & Lvalue" RCR should better be
    *very* convincing !

    > > I made 2 mistakes, self is not the Proc object, x gets scoped out.
    > > r.replace { x } would work, but then r = proc { x } is more obvious.
    > > I don't know how to get the current Proc object from the proc's body.
    > > Any clue ?

    >
    >Well, in general, you'll have
    >
    > r = lambda { ... }
    >
    >The block captures the scope in which it resides, so you can use r in the
    >block to refer to the Proc itself. That's the only way, as far as I know.


    Yes, apparently it is the only way. Using it in a safe way requires some care:
    begin
    r = lambda { ... use r here ... }
    end
    That way you are immune to further change to r, at least if r did not
    exist before. A safer way:
    begin
    my_own_very_specific_r_never_used_before = lambda { ... }
    end

    Yours,

    Jean-Hugues



    -------------------------------------------------------------------------
    Web: http://hdl.handle.net/1030.37/1.1
    Phone: +33 (0) 4 92 27 74 17
    Jean-Hugues ROBERT, May 4, 2004
    #13
  14. Jean-Hugues ROBERT

    Dan Doel Guest

    On Tuesday 04 May 2004 2:42 am, Jean-Hugues ROBERT wrote:
    > Lets try (using Proc instead of Method, we both agree I think that a Proc
    > can act as an anonymous method).
    >
    > # Make it so that a Proc can be a valid lvalue:
    > class Proc; def []=(*a) self[*a] end end
    >
    > def trace_array( an_array, msg )
    > proc { |*args|
    > if args.length() == 1 then
    > p "#{msg}: Read access at pos #{args[0]}"
    > an_array[args[0]]
    > elsif args.length() == 2 then
    > p "#{msg}: Write access at pos #{args[0}"
    > an_array[args[0]] = args[1]
    > else
    > p "#{msg}: Weird access."
    > an_array[*args[0...-1]] = args[-1]
    > end
    > }
    > end
    >
    > def buggy_meth()
    > a = [1,2,3]
    > a = trace_array( a, "a in buggy_meth()") if $Debug
    > ... use a ...
    > end


    Ah. Well, you have an example where #[]= makes sense for a Proc. However,
    you have specifically written your Proc to work differently based on the
    number of arguments so that it does different things for [a] and [a] = b. Most
    Proc objects aren't this way.

    This is more of a "Socrates is a man => All men are Socrates" situation. Just
    because you have examples where #[]= makes sense doesn't mean it makes sense
    in general. All Arrays, Strings and Hashes have indexed elements. Not all
    Proc objects pretend to. So really, you should only implement #[]= for Procs
    you specifically build to work this way (which you can do in Ruby).

    However, this doesn't mean it should be a general property of all Procs.

    > In this example you can substitute a Proc where an Array was expected.
    > The Proc is invoked both for read and write accesses to the Array.
    > It outputs a msg and then performs the required operation on the Array.
    >
    > This is a simple debugging tool made possible thanks to an additional
    > level of indirection.


    Incidentally, the Proc won't respond to all the methods of Array. It's
    probably better to redefine the #[] and #[]= methods of that specific Array
    to do the logging, rather than wrap it in a proc that pretends to be an
    Array for two methods. So the Proc method isn't necessarily the best way to do
    it.

    > > a[i, j, k] = l, m, n #=> becomes a.call(i, j, k, [l, m, n])

    >
    > Its actually # => becomes a.call( i, j, k, l).


    :) No, it does what I said. Try it out.

    > We both agree I think that o.x = y is a nicer syntax then o.x( y).
    > Only I think that p[] = y is a nicer syntax then p[y] if proc were an
    > accessor.


    But p isn't always an accessor. Your #[]= proposal is similar to saying
    that because we can call p.a, we should always be able to call p.a = b.
    There are many cases where p.a = b doesn't make sense, whether it's
    because the attribute is read only, or because #a isn't an attribute at
    all. For example, if you can call:

    foo.compute_interest

    Does it make sense to say:

    foo.compute_interest = 6

    ? Now, what if you do

    m = foo.method:)compute_interest)

    Does m[] = 6 make sense? If it doesn't make sense for all cases, you
    shouldn't automatically define it for all cases.

    > Another one:
    > If ptr = Pointer.new(...) then p[] = x is nicer syntax (to me) than p.set(
    > x) because
    > to me it reads "the content of p is assigned the value of x".


    But a Pointer is not a Proc. Pointers have content that you can read/write.
    The 'content' of a Proc is some code and the context that code is from. You
    can't write to it (directly). You can only execute it and get the return
    value.

    > Back to block: b[] = x, to me, reads as "the content of b is assigned the
    > value of x".
    > What that means exactly depends on the semantic of the block b.


    But b[] isn't "the content of b" for Proc objects. It's "call b." They're
    different conventions.

    In C++, I can do

    cout << "Hello.";

    << also works on ints. Does this make sense?

    a = 5 << "Hello";

    Just because it's the same symbols (<< or []) doesn't mean it does the same
    thing. [] for Array means something different than [] for Proc. It's handy
    that they can be used interchangably in some circumstances, but that doesn't
    mean they're interchangeable in all circumstances. Just because Arrays have
    [] doesn't mean I expect them to define #call like a Proc. So just because
    Procs have [] doesn't mean they should have []= like an array.

    > My conclusion is:
    > When x[ii] means "content of x" and x[ii]= y means "content of x is
    > assigned the value of y",
    > it makes sense that x can be anything, a Method or a Block included,
    > because I should
    > not care about that, it is up to the implementation to decide.
    > As a consequence it makes sense to define Proc##[]= as much as Proc##[] and
    > def []=(*a) self[*a] end makes sense as a default implementation.


    x[ii] doesn't always mean "content of x." Ruby doesn't define what [] means
    for every object. I can make it mean whatever I want:

    class Foo
    def [](x)
    x + 5
    end
    end

    Now, Foo.new[x] just means "x + 5". []= here has no meaning. There would
    be no assignment. Does this make sense:

    Foo.new[] = x <==> x + 5

    It certainly doesn't make sense to me. It looks like assignment, but it's just
    adding 5 to x. Now consider the following block:

    lambda { |x| x + 5 }

    This is the same as Foo above.

    > If c is_a? Lvalue then the lvalue object that c refers to (& which can be
    > any lvalue,
    > a variable included), becomes a new Foo (which means that it now refers to
    > a Foo:
    > c now refers to the same something but that something now refers to the new
    > Foo).


    So the interpreter has to do runtime checks to see if a variable has an object
    of type Lvalue and then does special things if it is? What if I want to make
    my own class that has special assignment characteristics, but don't want to
    inherit from Lvalue? I can't.

    > However, this does not mean that all Ruby variables would have to be
    > different from what they are today (whatever the name you use to describe
    > what they are today). Only variables that holds a reference to a Lvalue
    > object would have to be treated differently than the "normal" variables.
    > That's because the Ruby interpretor would have to invoke some .getter() or
    > .setter() method of the Lvalue instead of using the variable's content
    > directly (or, to rephrase more formally, xxx instead of directly using the
    > reference to some object that the variable holds).


    How do you determine at compile time whether

    a = b

    means "a = b" or "a.setter(b.getter)"? The only answer I can see is, "it's
    always 'a.=(b)'." Which means by-value assignment. The only way you could
    keep Ruby's existing assignment semantics, is if 'a.=(b)' was by default
    'a.become(b)'.

    > I am not proposing such a radical change at all. I would rather go forward
    > than backward :) What I am proposing is an additional tool, by the way of
    > an additional level of indirection. When the programmer need that tool it
    > has to be explicit and she/he would create a Lvalue object using some
    > explicit syntax:
    > b = "toto"
    > c = ref b # *explicit*
    > c = "titi"
    > p b # => "titi"
    > c is like an alias for b.


    So is c a different type of variable than b? Does this mean we have regular
    type variables and reference type variables? Are reference type variables
    only able to have their references set at time of definition? How does this
    work:

    a = "toto"
    b = "frodo"
    c = ref b
    c = ref a

    At the end, what is b? Is b a reference to a? Is b still "frodo"? If you
    pick one, what if I want the other? Also, what about:

    a = "toto"
    c = "frodo"
    c = ref a

    How does the compiler know which type of variable c is, because it holds both
    regular and reference types at various times?

    > The implementation of Lvalue that I am propotyping does not use #become
    > (#become BTW is not yet fully available I believe, but that is not the
    > reason).
    >
    > b = "toto"
    > p b.object_id() # 123
    > c = ref b
    > c = "titi"
    > p b.object_id() # 456
    > If I were to use #become, b.object_id() would stay the same. It's not the
    > case and must not be.
    >
    > As a matter of fact, I am very unsure that a Lvalue class could be
    > implemented at all using #become.


    Perhaps #become was the wrong way of putting it.

    Here's what I'm saying. By distinguishing between regular and reference
    variables or whatever, what you're saying is that "A variable represents a
    chunk of memory." Currently, Ruby says, "A variable is a reference to an
    object." For your proposal to work, 'a = b' for reference variables would
    mean, "copy b's memory into the place pointed by a." Currently, it's
    "make a point to the same place as b." In your proposal, 'a = b' for regular
    variables (and there is a distinction) would mean "copy b's memory into
    a's memory."

    I guess what you want is to keep "every variable is a reference" and you
    want reference references. Or something like that. You've already built
    that with your pointer class, it just isn't as transparent as regular
    assignment. But in my mind, that's okay, because what you want isn't
    useful/the correct way to do things very often. If we had what you
    envision, we could have people writing:

    foo(a, ref b)

    instead of

    b = foo(a)

    Which is bad. It's like C. The only reason you write C code like that is
    to return error codes while still passing out information, or to pass out
    multiple values. We have exceptions for error conditions, and we can
    easily return out multiple values. So variable references are, in fact,
    _the incorrect Ruby-way to do things_ for the two biggest cases of their use
    in C.

    > Then you don't mind that much that "In Ruby everything is an object, but
    > variables and
    > ... and ...".
    > I would prefer "In Ruby everything is an object". Introspection is a great
    > tool, the more, the better.


    To me, variables in Ruby are imaginary. They only help me, and don't exist
    as far as the objects/interpreter are concerned. The only way to get an
    object is by it's object id, and a variable holds an id for my convenience.

    Saying 'a = b' copies the id from b to a. Then 'a.foo' means the interpreter
    actually looks up the object with id stored in a, and calls its method foo. Of
    course, it's more efficient than that, but it works from a conceptual point
    of view.

    Ruby programs are collections of interating objects, not collections of
    interacting variables. Variables just make things easier for me to read when
    I'm telling the objects what to do. Whether or not an object realizes that
    it's method contains a local that I called "bar" doesn't matter as long as the
    method returns the right result. It could rename my variable to "baz" or
    "#0526ABFC" for all I care.

    I doubt reference variables would be used for introspection. You can already
    get and set instance variables by a method. What is the purpose of knowing
    that a method has a local variable called "x"? Being able to assign to a
    variable in one place and have it affect a variable in an entirely different
    would, in my opinion, be more often confusing than useful. Your logic
    methods are the only good example I know of, and you can implement them
    in pure Ruby as long as you don't mind using explicit dereferencing and some
    eval evil. I don't think they would be generally useful, because there are
    better ways to do most of what they let you accomplish.

    Cheers.

    - Dan
    Dan Doel, May 5, 2004
    #14
  15. At 09:16 05/05/2004 +0900, you wrote:
    > > > a[i, j, k] = l, m, n #=> becomes a.call(i, j, k, [l, m, n])

    > > Its actually # => becomes a.call( i, j, k, l).

    >:) No, it does what I said. Try it out.


    You are right, I was wrong.
    Fortunately a[i, j, k], b = l, m, n *does* become
    a.call( i, j, k, l) plus b = m.

    I was worried that multiple assignments would not work specifically
    with []=, but it does work, as long as both sides of = are "multiple".

    > > We both agree I think that o.x = y is a nicer syntax then o.x( y).
    > > Only I think that p[] = y is a nicer syntax then p[y] if proc were an
    > > accessor.

    >
    >But p isn't always an accessor. Your #[]= proposal is similar to saying
    >that because we can call p.a, we should always be able to call p.a = b.


    The thing is, the one that use a block usually knows about what the
    block semantic is and what is the protocol to use it (including what params the
    block expects). My proposal is just a convenience thing so that
    you can keep doing x = proc { xxx } instead of some x = LvalueProc.new { xxx },
    when your proc can be a lvalue in addition to a rvalue. Not a big deal really.

    > > Back to block: b[] = x, to me, reads as "the content of b is assigned the
    > > value of x".
    > > What that means exactly depends on the semantic of the block b.

    >
    >But b[] isn't "the content of b" for Proc objects. It's "call b." They're
    >different conventions.


    That is the way it is today, I am proposing a small change where in addition
    to [] meaning "value of" or "call" there would be []= meaning "gets
    assigned xx".
    This would be an additional convention/meaning that the user may want to attach
    to the semantic of the block. It would not work for read only block,
    much like Konst = x does not work for constants. I understand that you
    prefer []= to be undefined by Proc because there are blocks which are
    read-only and as a result []= is meaningless for them whereas [] allways make
    sense because a block is always "callable", if you know what parameter to use.
    At the end of the day it the user of the block that knows what can be done,
    not class Proc. I suppose we may safely assume that []= makes no sense, never,
    with parameterless blocks.

    >In C++, I can do
    > cout << "Hello.";
    ><< also works on ints. Does this make sense?
    > a = 5 << "Hello";
    >Just because it's the same symbols (<< or []) doesn't mean it does the same
    >thing. [] for Array means something different than [] for Proc. It's handy
    >that they can be used interchangably in some circumstances, but that doesn't
    >mean they're interchangeable in all circumstances.


    Sure. In some circumstances I do believe it is "handy" that a Proc could
    implement
    slightly more of the protocol of Array. That is possible by some user defined
    ArrayLikeProc subclass of Proc. That class Proc would implement []= would just
    be convenient, a convenience that is questionable and is maybe a matter of
    taste.

    >Just because Arrays have
    >[] doesn't mean I expect them to define #call like a Proc. So just because
    >Procs have [] doesn't mean they should have []= like an array.


    Symmetry for symmetry is not a good enough reason, I agree with you.

    On a similar plane but for rvalue, I think Smalltalk as a xx.value() method
    that
    a lot of objects implement. Such objects are called Valueables I think.
    This is handy. I think Smalltalk .value() for blocks/procs would be
    equivalent to
    xx.call().

    About an hypothetical Lvalue class:
    c = Foo.new()
    > > If c is_a? Lvalue then the lvalue object that c refers to (& which can be
    > > any lvalue,
    > > a variable included), becomes a new Foo (which means that it now refers to
    > > a Foo:
    > > c now refers to the same something but that something now refers to the new
    > > Foo).

    >
    >So the interpreter has to do runtime checks to see if a variable has an object
    >of type Lvalue and then does special things if it is? What if I want to make
    >my own class that has special assignment characteristics, but don't want to
    >inherit from Lvalue? I can't.


    How the interpretor determines that an object references another one
    is to be refined. Obviously a Lvalue object would reference another object.
    As a result the Interpretor could handle assignment doing something like:
    if (target_var = exiting_var).kind_of? Lvalue then
    target_var = existing_var_current_value
    end
    assign new_value to target_var

    OTOH the interpretor could use .respond_to? := instead of .kind_of? Lvalue.

    In the first case, you are right, if you want to make your own class that
    has special assignment characteristics, you have to inherit from Lvalue.

    In the second case you merely have to define .=() in your own class.

    It is probable that there are other ways for the interpretor to determine
    that it has to do an additional level of dereferencing. Each way would have
    pros/cons. I would tend to pick a solution with minimal speed overhead because
    the check would occur frequently.

    Please note that I believe that a Lvalue class would be usefull even
    without any auto-dereferencing by the interpretor. Auto dereferencing is
    just something that would make my prototype implementation of LogicPointer
    more transparent (much as can be done with delegation on some other plane).

    > > However, this does not mean that all Ruby variables would have to be
    > > different from what they are today (whatever the name you use to describe
    > > what they are today). Only variables that holds a reference to a Lvalue
    > > object would have to be treated differently than the "normal" variables.
    > > That's because the Ruby interpretor would have to invoke some .getter() or
    > > .setter() method of the Lvalue instead of using the variable's content
    > > directly (or, to rephrase more formally, xxx instead of directly using the
    > > reference to some object that the variable holds).

    >
    >How do you determine at compile time whether
    >
    > a = b
    >
    >means "a = b" or "a.setter(b.getter)"? The only answer I can see is, "it's
    >always 'a.=(b)'." Which means by-value assignment. The only way you could
    >keep Ruby's existing assignment semantics, is if 'a.=(b)' was by default
    >'a.become(b)'.


    I think that you are right, you cannot determine it easely at compile time.
    I believe you should to do it at runtime.

    But the interpretor can optimize that a lot because most of the time the
    full blown
    Lvalue object is not needed by the user.

    > > I am not proposing such a radical change at all. I would rather go forward
    > > than backward :) What I am proposing is an additional tool, by the way of
    > > an additional level of indirection. When the programmer need that tool it
    > > has to be explicit and she/he would create a Lvalue object using some
    > > explicit syntax:
    > > b = "toto"
    > > c = ref b # *explicit*
    > > c = "titi"
    > > p b # => "titi"
    > > c is like an alias for b.

    >
    >So is c a different type of variable than b? Does this mean we have regular
    >type variables and reference type variables?


    Not exactly. What happens is that a variable can reference a Lvalue object.
    When assigning a value to a variable, the interpretor would check the previous
    value of the variable. If the variable references an Lvalue object (or, maybe,
    an object that responds to := ) then it would be the Lvalue object that is
    told to
    reference a new value instead of the initial variable.

    > Are reference type variables
    >only able to have their references set at time of definition? How does this
    >work:
    >
    > a = "toto"
    > b = "frodo"
    > c = ref b
    > c = ref a
    >
    >At the end, what is b? Is b a reference to a? Is b still "frodo"? If you
    >pick one, what if I want the other? Also, what about:
    >
    > a = "toto"
    > c = "frodo"
    > c = ref a
    >
    >How does the compiler know which type of variable c is, because it holds both
    >regular and reference types at various times?


    Very good questions indeed.

    I believe that
    c = ref b
    c = ref a
    implies that at the end b is a Lvalue that references the variable a (and
    c stays unchanged)

    If you want the other you need to use the RCR's "assign" operator:
    assign c, ref a.

    That will be described further in the "match, assign & Lvalue" RCR that
    I am working on.

    >Here's what I'm saying. By distinguishing between regular and reference
    >variables or whatever, what you're saying is that "A variable represents a
    >chunk of memory." Currently, Ruby says, "A variable is a reference to an
    >object." For your proposal to work, 'a = b' for reference variables would
    >mean, "copy b's memory into the place pointed by a." Currently, it's
    >"make a point to the same place as b." In your proposal, 'a = b' for regular
    >variables (and there is a distinction) would mean "copy b's memory into
    >a's memory."


    I was not clear enough in my explanations. Thanks to the Lvalue class, a
    variable
    can sometimes reference another variable. At the end of the day *all* variables
    reference an object, exactly as it is the case with Ruby today.

    There would be no such thing as a "regular" versus a "reference" variable.
    There would be only "regular" variables, but some of them would reference
    an object of class Lvalue instead of an object of some other class.

    >I guess what you want is to keep "every variable is a reference" and you
    >want reference references. Or something like that. You've already built
    >that with your pointer class, it just isn't as transparent as regular
    >assignment. But in my mind, that's okay, because what you want isn't
    >useful/the correct way to do things very often. If we had what you
    >envision, we could have people writing:
    >
    > foo(a, ref b)
    >
    >instead of
    >
    > b = foo(a)
    >
    >Which is bad. It's like C. The only reason you write C code like that is
    >to return error codes while still passing out information, or to pass out
    >multiple values. We have exceptions for error conditions, and we can
    >easily return out multiple values. So variable references are, in fact,
    >_the incorrect Ruby-way to do things_ for the two biggest cases of their use
    >in C.


    I understand that any features can be poorly used. That something can be
    used in a bad way is also the responsibility of the user (assuming that the
    intended "good" usage was properly documented). I believe that a Lvalue class
    is actually something that makes it possible to do things in a cleaner object
    oriented way than what you can do today with more questionable mechanisms,
    eval() included. It may also be a tool that makes it easy to do things that
    are really difficult to do in Ruby.

    The Lvalue class is to lvalues what the Method class is to methods.

    Think about Ruby's local_variables(), it returns an array of strings. Then
    you typically use the strings with eval(). A Lvalue class is kind of
    implemented there, with strings object ids in some parallel object space.

    Imagine now a local_variables() that would return an array of Lvalue
    instances. Now, instead of using eval() to get/set the local variables, you
    would use the Lvalue objects' getter()/setter() methods.

    I think most people would agree that you should use eval() only when
    there is no better way to do something (when there is no better way,
    you are really happy that at least eval() does exist !).

    A Lvalue class is I believe something useful to do things that can
    be done with eval() only, as of today. It helps make these things
    in a more object oriented way. I suspect it makes it possible to
    do things that are really difficult to do today (never say
    "impossible").

    I hope it is clearer now that my intention is to help people do things
    in a cleaner object oriented way. I don't feel responsible for the
    perverse use of some new Lvalue class as long as my mechanism is
    well designed enough to work well for natural "good" usages.

    > > Then you don't mind that much that "In Ruby everything is an object, but
    > > variables and
    > > ... and ...".
    > > I would prefer "In Ruby everything is an object". Introspection is a great
    > > tool, the more, the better.

    >
    >To me, variables in Ruby are imaginary. They only help me, and don't exist
    >as far as the objects/interpreter are concerned. The only way to get an
    >object is by it's object id, and a variable holds an id for my convenience.


    This is most often the case. It is only when you start toying with
    introspection
    and meta-programming that the Lvalue class starts becoming interesting.

    >I doubt reference variables would be used for introspection. You can already
    >get and set instance variables by a method. What is the purpose of knowing
    >that a method has a local variable called "x"? Being able to assign to a
    >variable in one place and have it affect a variable in an entirely different
    >would, in my opinion, be more often confusing than useful. Your logic
    >methods are the only good example I know of, and you can implement them
    >in pure Ruby as long as you don't mind using explicit dereferencing and some
    >eval evil. I don't think they would be generally useful, because there are
    >better ways to do most of what they let you accomplish.


    Thanks about the "logic" methods. The prototype implementation is in progress,
    using explicit dereferencing (and some eval evil). How much useful it is
    may become more apparent in the future RCR unless I am overlooking better
    ways to do most of waht they let you accomplish.

    I suppose my intention has some connections with yours regarding evil eval.
    We both look for better ways to do things that only eval can do today.
    Now, about how much introspection is enough, it varies depending on your needs.

    >Cheers.
    >
    >- Dan


    Thanks for your feedbacks.

    Yours,

    Jean-Hugues



    -------------------------------------------------------------------------
    Web: http://hdl.handle.net/1030.37/1.1
    Phone: +33 (0) 4 92 27 74 17
    Jean-Hugues ROBERT, May 5, 2004
    #15
    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. David Lozzi
    Replies:
    3
    Views:
    1,932
    David Lozzi
    Jun 1, 2005
  2. Brian Blais
    Replies:
    1
    Views:
    376
    Bruno Desthuilliers
    Jun 27, 2006
  3. NevilleDNZ
    Replies:
    9
    Views:
    441
    NevilleDNZ
    Aug 16, 2006
  4. Mr. SweatyFinger
    Replies:
    2
    Views:
    1,833
    Smokey Grindel
    Dec 2, 2006
  5. Minkoo Seo

    Proc vs lambda vs proc

    Minkoo Seo, Feb 4, 2007, in forum: Ruby
    Replies:
    19
    Views:
    241
    Brian Candler
    Feb 6, 2007
Loading...

Share This Page