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

  • Thread starter Jean-Hugues ROBERT
  • Start date
J

Jean-Hugues ROBERT

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
 
D

Dan Doel

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
 
J

Jean-Hugues ROBERT

About class Proc; def []=(*args); self[*args] end end and the value of
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
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.

Yours,

Jean-Hugues
 
M

Mauricio Fernández

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
 
D

Dan Doel

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
 
J

Jean-Hugues ROBERT

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
 
D

Dan Doel

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
 
Z

Zsban Ambrus

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

Jean-Hugues ROBERT

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
 
J

Jean-Hugues ROBERT

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

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.

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 ?

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 !).
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
 
D

Dan Doel

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
 
J

Jean-Hugues ROBERT

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
 
J

Jean-Hugues ROBERT

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:

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.
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 !
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
 
D

Dan Doel

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
 
J

Jean-Hugues ROBERT

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()
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).
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.
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.
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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,768
Messages
2,569,575
Members
45,053
Latest member
billing-software

Latest Threads

Top