Submitted by tunah (Thu Jul 29 08:07:05 UTC 2004)
A keyword "original" which could be used to call the previous definition of the current method in this class, similar to the keyword "super" which is used to call the definition of the current method in the superclass. This makes redefining a method a lot nicer in many cases.
Suggestiong for a better name than "original" are welcomed.
Ruby allows you to redefine methods of some class in an unrelated section of code. Often it is desired to use this to add one feature to a method.
For example, I might want to introduce a new numeric type, and allow Fixnum#+ to accept it. There is currently no simple way to refer to the "old" behaviour of Fixnum#+ inside my new definition, and so I resort to something like:
class Fixnum alias :old_plus :+ def +(x)if x.is_a? MyType ... else old_plus(x) endend end
Except that I have to take care to use a unique method name instead of "old_plus", in case another library does this too and the names collide.
This is cumbersome, and results in namespace pollution.
Add a keyword "original".
It would have the same syntax as "super", and its behaviour would be:
If the method with the current method's name was defined in the current method's class immediately prior to the def statement that defined the current method, then _that_ method is executed.
Otherwise, it behaves identically to "super"
The above example would become:
class Fixnum def +(x)if x.is_a? MyType ... else original(x) endend end
The method aliasing method is ugly, not obvious to beginners, and just doesn't "feel right".
In most other common OO languages, methods can only be defined once, and so the equivalent of "super" refers to "the definition I'm overriding". Being able to add functionality like that without getting deep into the class's implementation is important for decoupled code.
There may be a case for making "super" behave like "original" instead of the current behaviour, or replacing it with "original", but that would likely be too disruptive.
Potential problems:
If "original" is called in Foo#meth and there was no previous definition, Foo#method_missing will get called with method name :meth. This is potentially confusing since the method does exist. I still think this is the best behaviour.
A backwards compatibility exists due to the introduction of a new keyword.
Performance/memory issues if uses of "original" cannot be identified (see Implementation)
(Note: I know very little about the implementation of Ruby, so please correct me if I'm wrong)
Excluding the case of eval-ing a string, I think it is possible to determine when parsing the method definition whether "original" is used. If it is not used, then the previous definition does not need to be stored, and so there should be negligible performance loss.
If it is used, the method could store a pointer to the "previous" method. When "original" is invoked in a method, it would invoke the "previous" method if non-null, otherwise it would behave as super does now. The performance cost should be less than when using the aliasing solution (due to being written in C and the fact that if the chain is broken all "orphan" methods get destroyed).
The problem with this is if eval(string) is used, it can't be determined if "original" will be called. Two options are:
a) Disallow "original" in eval(string). This makes it inconsistent with "super".
b) Always store the previous method if eval(string) is used. This increases the cost of eval() in cases where "original is not used".
Comments | Current voting | ||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|
|
This RCR supersedes RCR 264.
RCRchive copyright © David Alan Black, 2003-2005.
Powered by .
I'm not sure if this is needed with the :pre and :post hooks that Matz will be adding and the changes to super that will entail. There are questions about how one will *replace* a method rather than wrap, so ... -- Austin Ziegler
LOL, you beat me to this, Austin. FWIW, the slide on method hooks from Matz's 2003 rubyconf presentation is this one:
-- Paul Brannan
Oops, I hadn't seen that. The only thing you can't seem to do there is use "super" and "original" in the same method, but I'm not sure if that's useful. I'll have a think about it, and probably withdraw this. -- tunah
Hopefully those hooks will provide what you need, but just a quick comment anyway:
There's a big difference between super and what you're proposing: namely, in the case of super, you're searching up the method lookup path in the normal way, but skipping one level. When you redefine a method, however, the old definition is removed from the method lookup path -- so what you're proposing is a much more fundamental change (i.e., not really directly analogous to super), because you're suggesting that the method lookup path be restructured to do a kind of lateral sweep before it starts moving upward, so to speak. -- David Black
I realise that the implementation would be very different, I meant the syntax would be similar, and the semantics would be related. It wouldn't be a sweep if I understand you correctly, it would be an upward chain in reverse order of definition (which is then joined to the inheritance hierarchy). Anyway, the point's probably moot :-) --tunah