method search rule in 2.0?

D

David Garamond

I read:


http://pub.cozmixng.org/~the-rwiki/rw-cgi.rb?cmd=view;name=Ruby2.0MethodSearchRuleEnglish

and was feeling disturbed about this new Ruby2 behaviour.

class C
def process
# ...
util
end

def util
# ...
end
end

class CC < C
def util
# ...
end
end

CC.new.process # C#process expects C#util,
# but calls CC#util

Why is this a problem? Isn't it what people expect in OO? I'm not an OO
expert, but isn't this one of the supposedly unique feature/benefit of
OO: allowing old code to call new code. I believe it's called polymorphism?

Now Ruby2 wants to change so that 'util' in C method calls C#util and
'self.util' calls CC#util. I very much prefer 'self.util' to be the one
that calls C#util (and I don't think I'll ever use it a lot). The latter
is backwards compatible and how virtually all other languages behave.

Regards,
dave
 
P

Peter C. Verhage

David said:
Now Ruby2 wants to change so that 'util' in C method calls C#util and
'self.util' calls CC#util. I very much prefer 'self.util' to be the one
that calls C#util (and I don't think I'll ever use it a lot). The latter
is backwards compatible and how virtually all other languages behave.

I totally agree with you.

I think a better way of solving this problem is using private methods
which are only part of the class for which they are defined. If a
subclass defines a method (private, protected or public) with the same
name as the private method in the superclass then all calls to the
private method made by methods in the superclass call the private method
in the super class. All methods calls on the method in the subclass call
the method defined there. Public or protected methods defined in
superclasses can be overriden in subclasses and when they are called
from the superclass the overriden method will be called. I believe this
is the way Java and C++ (among others) work.

Regards,

Peter
 
D

David A. Black

Hi --

I read:


http://pub.cozmixng.org/~the-rwiki/rw-cgi.rb?cmd=view;name=Ruby2.0MethodSearchRuleEnglish

and was feeling disturbed about this new Ruby2 behaviour.

class C
def process
# ...
util
end

def util
# ...
end
end

class CC < C
def util
# ...
end
end

CC.new.process # C#process expects C#util,
# but calls CC#util

Why is this a problem? Isn't it what people expect in OO? I'm not an OO
expert, but isn't this one of the supposedly unique feature/benefit of OO:
allowing old code to call new code. I believe it's called polymorphism?

Now Ruby2 wants to change so that 'util' in C method calls C#util and
'self.util' calls CC#util. I very much prefer 'self.util' to be the one that
calls C#util (and I don't think I'll ever use it a lot). The latter is
backwards compatible and how virtually all other languages behave.

I agree that the non-special case should be what it is now, and the
case where you don't want overriding should be the one that requires
something extra. But I don't think it should be based on overloading
"self" (and I believe having "self" refer to the class context, rather
than the object, is a kind of overloading). If this mechanism is
really necessary, I would rather see:

class C
def process
# ...
C#util
end
end

which is, I think, a more direct way of saying: protect this call from
overriding in subclasses. (Yes, everybody, I do know that this is
comment syntax and would require a parser change :)


David
 
C

Csaba Henk

class C
def process
# ...
C#util
end
end

which is, I think, a more direct way of saying: protect this call from
overriding in subclasses. (Yes, everybody, I do know that this is
comment syntax and would require a parser change :)

As by the ruby of today, how you can do the above is:

class C
def process
# ...
C.instance_method:)util).bind(self).call
end
end

Now you will agree that it is not particularly convenient; but this is
how to do it... But. It's sooo long that you can't help remembering the
occasion if you use it.

And I don't remember doing it more that one or two times. That is, my
experience suggests that it's not a particularly frequent pattern. Which
means I agree with those who like the present state of the art.

Csaba
 
L

Lionel Thiry

David A. Black a écrit :
I agree that the non-special case should be what it is now, and the
case where you don't want overriding should be the one that requires
something extra. But I don't think it should be based on overloading
"self" (and I believe having "self" refer to the class context, rather
than the object, is a kind of overloading). If this mechanism is
really necessary, I would rather see:

class C
def process
# ...
C#util
end
end

which is, I think, a more direct way of saying: protect this call from
overriding in subclasses. (Yes, everybody, I do know that this is
comment syntax and would require a parser change :)


David

class C
def process
# ...
C::util
end
end

equivalent to

class C
def process
# ...
self.C::util
end
end

equivalent to

class C
def process
# ...
self.(C::util)
end
end
 
T

Trans

Ah, this is interesting. In the AOP work I've been doing, something
along these lines is a absolute must inorder to prevent method name
clashes and thus have fully reusable Aspects. My solution was to have
another scoping mechinism akin to public, private, protected, called
"local". It differs in that methods defined in the local scope are
always called locally in the context of that module, but not in the
context of any other. For instance:

class C
def x ; 1 ; end
end

class D
def y ; x ; end
local
def x ; 2 ; end
end

d = D.new
d.y #=> 2
d.x #=> 1

T.
 
D

David A. Black

--927295978-61296759-1113063951=:8142
Content-Type: MULTIPART/MIXED; BOUNDARY="927295978-61296759-1113063951=:8142"

This message is in MIME format. The first part should be readable text,
while the remaining parts are likely unreadable without MIME-aware tools.

--927295978-61296759-1113063951=:8142
Content-Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed
Content-Transfer-Encoding: QUOTED-PRINTABLE

Hi --

David A. Black a =E9crit :

class C
def process
# ...
C::util

That's different; that's a class method.
end
end

equivalent to

class C
def process
# ...
self.C::util
end
end

equivalent to

class C
def process
# ...
self.(C::util)
end
end

These last two don't really fit the dot semantics, and if they're
alternatives to C::util, then they're dealing with a class method
anyway (rather than the issue of limiting the effect of overriding
instance methods).

My suggestion of C#util is based on the common use of # to mean
"instance method of the named class or module". It wouldn't require
redefinition of the dot notation, nor overloading 'self' to be a
boolean flag (as in the original proposal, where 'self' means both
"the default object" and "don't redirect this to an overridden version
of the method).


David

--=20
David A. Black
(e-mail address removed)
--927295978-61296759-1113063951=:8142--
--927295978-61296759-1113063951=:8142--
 
L

Lionel Thiry

David A. Black a écrit :
Hi --




That's different; that's a class method.

In present ruby, yes, C::util is identical to C.util.

But I was thinking of "C::util" as an explicit namespace specification, not as a
message sending to C object.

You talked about adding some "#" new operator could be inadequate as it is
usually used for comments. I was just indicating "::" could do the job.

Sorry for my lack of clarity.
These last two don't really fit the dot semantics, and if they're
alternatives to C::util, then they're dealing with a class method
anyway (rather than the issue of limiting the effect of overriding
instance methods).

My suggestion of C#util is based on the common use of # to mean
"instance method of the named class or module". It wouldn't require
redefinition of the dot notation, nor overloading 'self' to be a
boolean flag (as in the original proposal, where 'self' means both
"the default object" and "don't redirect this to an overridden version
of the method).

And what is your opinion, now, if you take for granted that "::" semantic is
redefined?
 
P

Paul Hanchett

David said:
Hi --




I agree that the non-special case should be what it is now, and the
case where you don't want overriding should be the one that requires
something extra. But I don't think it should be based on overloading
"self" (and I believe having "self" refer to the class context, rather
than the object, is a kind of overloading). If this mechanism is
really necessary, I would rather see:

class C
def process
# ...
C#util
end
end

which is, I think, a more direct way of saying: protect this call from
overriding in subclasses. (Yes, everybody, I do know that this is
comment syntax and would require a parser change :)

If I was writing class C, I would be surprised when CC#util got called,
but that is exactly the OO behavior you want sometimes. I think both
the naked call to util and self.util should be relative to the
/instantiated/ object.

If I really want to call C#util, why not say C::util? It seems like
anything else has compatibility problems...

Paul
 
D

David A. Black

Hi --

That's different; that's a class method.

In present ruby, yes, C::util is identical to C.util.

But I was thinking of "C::util" as an explicit namespace
specification, not as a message sending to C object.

You talked about adding some "#" new operator could be inadequate as
it is usually used for comments. I was just indicating "::" could do
the job.



And what is your opinion, now, if you take for granted that "::"
semantic is redefined?

I think it's much too big a change for something like this. It would
also lead to huge amounts of code breakage. (I don't use :: for
method calls myself, but it's used a fair amount.)


David
 
D

David A. Black

Hi --

If I was writing class C, I would be surprised when CC#util got
called, but that is exactly the OO behavior you want sometimes. I
think both the naked call to util and self.util should be relative to
the /instantiated/ object.

I agree. That's why I don't like the proposed change to "self".
If I really want to call C#util, why not say C::util? It seems like
anything else has compatibility problems...

C::util is a class method call, but what we're talking about here
pertains to instance methods. The idea of C#util is not to call
C::util, but to call util on the instatiated object -- constraining
that call so that it is the #util defined in class C, not class CC.

class C
def x
puts "I'm the #x from C"
end

def y
x # call the most recently defined #x at the time of the call
end

def z
C#x # call the x defined in C, no matter what
end
end

class CC < C
def x
puts "I'm the #x from CC"
end
end

CC.new.y # I'm the #x from CC
CC.new.z # I'm the #x from C

Note that having a CC object "call the x defined in C" is different
from saying "Call the class method C.x" (which doesn't exist at all).


David
 
T

Trans

David said:
class C
def x
puts "I'm the #x from C"
end

def y
x # call the most recently defined #x at the time of the call
end

def z
C#x # call the x defined in C, no matter what
end
end

class CC < C
def x
puts "I'm the #x from CC"
end
end

CC.new.y # I'm the #x from CC
CC.new.z # I'm the #x from C

If you don't mind David, I think this is good example to show what I
mean by a local namespace too. The local namespace idea is sort like
the original idea (differentiating on using self or not) but remains
backward compatible.

class C
def x
puts "I'm the #x from C"
end
local :x # localize x to C

def y
self.x # call the most recently defined #x
# in this case using self forces a non-local call
end

def z
x # call the x defined in C, b/c x is local
end
end

class CC < C
def x
puts "I'm the #x from CC"
end
end

CC.new.y # I'm the #x from CC
CC.new.z # I'm the #x from C

Hope that makes my idea a bit clearer. This approach has an advantage
over notations like C#x also, in that modules can then be included into
a class' local space.

module CM
def x
puts "I'm the #x from CM"
end
end

class C
include_local CM
def x
puts "I'm the #x from C"
end
def z
x
end
end

CC.new.x # I'm the #x from C
CC.new.z # I'm the #x from CM

Note there isn't any name clash between the local and non-local x
methods (in fact that's the whole point) local methods are referenced
in there own namespace.

T.
 
P

Paul Hanchett

David said:
C::util is a class method call,

But if C#util is not defined as a with "def C.util ..." isn't it just a
regular method? Or is C::util really a different sort of call to the
class object?
but what we're talking about here
pertains to instance methods. The idea of C#util is not to call
C::util, but to call util on the instatiated object -- constraining
that call so that it is the #util defined in class C, not class CC.

class C
def x
puts "I'm the #x from C"
end

def y
x # call the most recently defined #x at the time of the call
end

def z
C#x # call the x defined in C, no matter what
end
end

class CC < C
def x
puts "I'm the #x from CC"
end
end

CC.new.y # I'm the #x from CC
CC.new.z # I'm the #x from C

Note that having a CC object "call the x defined in C" is different
from saying "Call the class method C.x" (which doesn't exist at all).

Allowing an inheriting class to call an ancestor method without
inheriting it (i.e., skipping the inheritance chain) is not a good idea,
I think. Of course with Ruby you can retroactively redefine existing
methods on instantiated objects, right? (I'm a Ruby NOOB, so I may have
that wrong!)

An underlying assumption about OOD is that ancestor classes don't need
to worry about descendent classes changing the meaning of methods
defined by the ancestors.

I think we will have to pick our poison-- Either there has to be a
declaration /in the class defintion/ whether a particular method is
virtual of not (C++), or we choose an object model where all methods are
virtual (Java and Python) or not (VB, I think).

Paul
 
D

David A. Black

Hi --

But if C#util is not defined as a with "def C.util ..." isn't it just a
regular method? Or is C::util really a different sort of call to the class
object?

C#util is actually the constant C plus a comment :) This is all
speculative; there's no C#util expression in reality.
Allowing an inheriting class to call an ancestor method without inheriting it
(i.e., skipping the inheritance chain) is not a good idea, I think.

I don't think that's what happening here (?).
Of
course with Ruby you can retroactively redefine existing methods on
instantiated objects, right? (I'm a Ruby NOOB, so I may have that wrong!)

Yes. Well, it's not really retroactive -- the old method calls don't
get re-performed :) It's perhaps better described as happening
dynamically. But, essentially, yes.
An underlying assumption about OOD is that ancestor classes don't need to
worry about descendent classes changing the meaning of methods defined by the
ancestors.

I think we will have to pick our poison-- Either there has to be a
declaration /in the class defintion/ whether a particular method is virtual
of not (C++), or we choose an object model where all methods are virtual
(Java and Python) or not (VB, I think).

I don't think the area that was addressed originally by the "self"
redefinition idea was that broad, design-wise -- that is, the basic
object model isn't under review, but just something very specific.
It's the following scenario: your superclass, inside an instance
method, calls another of its instance methods, expecting a particular
behavior, but that behavior has been changed by a subclass.


David A. Black
(e-mail address removed)
 
L

Lionel Thiry

David A. Black a écrit :
Hi --

[I wrote:]

That's different; that's a class method.

In present ruby, yes, C::util is identical to C.util.

But I was thinking of "C::util" as an explicit namespace
specification, not as a message sending to C object.

You talked about adding some "#" new operator could be inadequate as
it is usually used for comments. I was just indicating "::" could do
the job.

These last two don't really fit the dot semantics, and if they're
alternatives to C::util, then they're dealing with a class method
anyway (rather than the issue of limiting the effect of overriding
instance methods).

My suggestion of C#util is based on the common use of # to mean
"instance method of the named class or module". It wouldn't require
redefinition of the dot notation, nor overloading 'self' to be a
boolean flag (as in the original proposal, where 'self' means both
"the default object" and "don't redirect this to an overridden version
of the method).


And what is your opinion, now, if you take for granted that "::"
semantic is redefined?


I think it's much too big a change for something like this. It would
also lead to huge amounts of code breakage. (I don't use :: for
method calls myself, but it's used a fair amount.)

I use it for constants. Does anybody use it for something else?

If not, then only constants would be affected. And the namespace semantic
wouldn't change much how things work for constants, don't you think?

Or I am missing some points, somewhere, as usual. ;)
 
D

David A. Black

Hi --

I use it for constants. Does anybody use it for something else?

ruby/1.8$ grep "::[a-z_]" `find . -name "*.rb"` | wc -l
1194

:)
If not, then only constants would be affected. And the namespace semantic
wouldn't change much how things work for constants, don't you think?

I just think it would be awfully confusing.


David
 
A

Avdi Grimm

David Garamond said:
Now Ruby2 wants to change so that 'util' in C method calls C#util and
'self.util' calls CC#util. I very much prefer 'self.util' to be the one
that calls C#util (and I don't think I'll ever use it a lot). The latter
is backwards compatible and how virtually all other languages behave.

I would have to agree. As an OO programmer, the current behaviour is
what I would expect. And I'd go so far as to say that it's the
"correct" behaviour. When I call #util, I'm not "expecting" C#util, I'm
expecting the *right* #util to be called, where the "right" #util is
defined as the one that preserves the invariants of whatever class
self is a member of. An contrived example of why this is important:

class A

...

def do_stuff
data = get_some_data()
store(data)
end

def store(data)
@mydata << data
end
end

class ThreadSafeA

...

def store(data)
@mutex.lock
@mydata << data
@mutex.unlock
end
end

a = ThreadSafeA.new
a.do_stuff

In this case if the call to do_stuff resulted in a call to A#store, it
would violate the thread-safety invariant promised by ThreadSafeA.

Maybe not the best example, but it's a pattern which permeates OO
design. It's the Strategy pattern, really: a superclass defines some
broad, high-level methods which are implemented in terms of more
granular support methods; and then the implementation strategy is
defined by subclassing it and overriding the suport methods.

Actually, in my mind applying the principle of least astonishment
would lead to expecting store and self.store to have identical
behaviour. I would expect to have to use something like super.store
or A::store in order to specify that I want the superclass version of
store.
 
T

Trans

Avdi said:
Actually, in my mind applying the principle of least astonishment
would lead to expecting store and self.store to have identical
behaviour. I would expect to have to use something like super.store
or A::store in order to specify that I want the superclass version of
store.

I have to agree with you. While making a distinction around the use of
self.x verses x seems reasonable to some degree (after all they are
written differently), the old ambiguity of x= raises it's ugly head. To
refresh, x= would be interpreted as a local variable assignment and not
a method call, thus requiring the use of self.x= instead. B/c of this
x= and self.x= must remain equivalent.

But lets say we do adopt a differnt syntax as you suggest. Of the
options I think David's suggestion of M#x is the most obvious since
that's the standard way to refer to a method already. But even this has
problems in that it puts some limits on reusability. Consider:

module M
def x ; 1 ; end
def y1 ; M#x ; end
def y2 ; M#x ; end
def y3 ; M#x ; end
# etc.
end

Perhaps originally it wasn't intended to be used otherwise, but if we
later wanted to create a variation of M with an altered #x, we'd then
have a problem b/c ordinary code like:

class N
include M
def x ; 2 ; end
end

n = N.new
n.y1 #=> 1
n.y2 #=> 1
n.y3 #=> 1

won't work. To overcome we'd actually have to redefine #y1, #y2, #y3
.... and so on. Not good. Of course Ruby has meta-programming techinques
that could be used to get around this, but who wants to dig even
further into complexity? That's why I think a localized namespace would
be preferable, as it is reasonably straightforward and would allow for
both contingencies.

T.
 
D

David A. Black

Hi --

I have to agree with you. While making a distinction around the use of
self.x verses x seems reasonable to some degree (after all they are
written differently), the old ambiguity of x= raises it's ugly head.

They're written differently, but the whole self.x = y thing is just
the price we pay for, in every other situation, being allowed to drop
'self' as the receiver. It keeps things simple and clean, overall
(and it's really not *that* ugly; it's just a receiver :) So I
wouldn't want to look at that as a rationale for overloading it, which
would make it less simple.
To
refresh, x= would be interpreted as a local variable assignment and not
a method call, thus requiring the use of self.x= instead. B/c of this
x= and self.x= must remain equivalent.

But lets say we do adopt a differnt syntax as you suggest. Of the
options I think David's suggestion of M#x is the most obvious since
that's the standard way to refer to a method already. But even this has
problems in that it puts some limits on reusability. Consider:

module M
def x ; 1 ; end
def y1 ; M#x ; end
def y2 ; M#x ; end
def y3 ; M#x ; end
# etc.
end

Perhaps originally it wasn't intended to be used otherwise, but if we
later wanted to create a variation of M with an altered #x, we'd then
have a problem b/c ordinary code like:

class N
include M
def x ; 2 ; end
end

n = N.new
n.y1 #=> 1
n.y2 #=> 1
n.y3 #=> 1

won't work. To overcome we'd actually have to redefine #y1, #y2, #y3
.... and so on. Not good.

But that's the whole purpose of the thing (whether it's M#x or self.x
or whatever) -- to constrain the nested method call (the call to #x
inside M#y1 etc.) and protect it from the redefinition of #x. I'm not
convinced of the merits of it (I continue to tend to think that anyone
subclassing a class should know the names of that class's methods, and
not accidentally override them), but the idea is specifically to
provide that protection.


David
 
T

Trans

Hello--
On Tue, 12 Apr 2005, Trans wrote:
They're written differently, but the whole self.x = y thing is just
the price we pay for, in every other situation, being allowed to drop
'self' as the receiver. It keeps things simple and clean, overall
(and it's really not *that* ugly; it's just a receiver :) So I
wouldn't want to look at that as a rationale for overloading it, which
would make it less simple.

I don't find the look of it at all ugly, only the asymmetry of that
particular case. But I understand why it must be so.

Also I point out the behavior of calling a private method using self as
the specified reciever: an error. So there is some precedence for
certain distinctions here. But I certainly agree with you, I do not
think it would not be wise to overload it in the manner originally
suggested.
But that's the whole purpose of the thing (whether it's M#x or self.x
or whatever) -- to constrain the nested method call (the call to #x
inside M#y1 etc.) and protect it from the redefinition of #x. I'm not
convinced of the merits of it (I continue to tend to think that anyone
subclassing a class should know the names of that class's methods, and
not accidentally override them), but the idea is specifically to
provide that protection.

That's only one use case actually. Another is fine-grain control of
inheritance --one can direct methods directly to specific parents. But
I agree with your hesitation for the reason of its nonoopiness (funny
word ;) but that's the trade off. I offered the concept of locals to
gain a middle ground in that trade --not quite as powerful, but
maintains some reusability (a very useful reusability for AOP, I might
add).

Hmmm... now that I think about it more in this light it could also be
interesting to actually define methods in this direct notation as well,
and this might lessen the shortcomings.

class C
def x
"C"
end
end

C.new.x #=> C

def C#x
"CX"
end

C.new.x #=> CX

T.
 

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

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,234
Latest member
SkyeWeems

Latest Threads

Top