Velocity Reviews

Velocity Reviews (http://www.velocityreviews.com/forums/index.php)
-   Ruby (http://www.velocityreviews.com/forums/f66-ruby.html)
-   -   Why no Proc##[]=() ? Why no Proc##replace() ? (http://www.velocityreviews.com/forums/t814420-why-no-proc-why-no-proc-replace.html)

Jean-Hugues ROBERT 05-01-2004 06:02 PM

Why no Proc##[]=() ? Why no Proc##replace() ?
 
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




Dan Doel 05-01-2004 09:43 PM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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



Jean-Hugues ROBERT 05-02-2004 08:18 AM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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




Mauricio Fernández 05-02-2004 01:10 PM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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



Dan Doel 05-02-2004 10:21 PM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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



Jean-Hugues ROBERT 05-03-2004 04:45 AM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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




Dan Doel 05-03-2004 05:39 AM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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



Zsban Ambrus 05-03-2004 10:06 AM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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
>





Jean-Hugues ROBERT 05-03-2004 11:29 AM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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 05-03-2004 11:54 AM

Re: Why no Proc##[]=() ? Why no Proc##replace() ?
 
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





All times are GMT. The time now is 04:53 AM.

Powered by vBulletin®. Copyright ©2000 - 2014, vBulletin Solutions, Inc.
SEO by vBSEO ©2010, Crawlability, Inc.