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