How to call overridden methods (finally!)

I thought I had come up with a clever way to call a method which has been
overridden:

module Foo
def to_s
’just another Foo’
end
end

class Bar
include Foo

def to_s
  'proud to be a Bar'
end

def foo_to_s
  Foo.instance_method('to_s').bind(self).call
end

end

puts Bar.new.to_s
puts Bar.new.foo_to_s

And it works!

proud to be a Bar
just another Foo

However, if Foo is a class and Bar subclasses it, it doesn’t work:

proud to be a Bar
./test.rb:24:in bind': bind argument must be an instance of Foo (TypeError) from ./test.rb:24:infoo_to_s’
from ./test.rb:29

So, my question is this: Why must bind’s argument be an instance of Foo?
Why not just a kind_of Foo? Could this be changed? Easily?

Chris

See [ruby-talk:38180].

Paul

···

On Fri, Jan 03, 2003 at 03:21:21AM +0900, Chris Pine wrote:

So, my question is this: Why must bind’s argument be an instance of Foo?
Why not just a kind_of Foo? Could this be changed? Easily?

Actually, here’s another way to call the “super” of a method, but it’s less
general than my first solution.

class Foo
def to_s
’just another Foo’
end
end

class Bar < Foo
def to_s
’proud to be a Bar’
end

def foo_to_s
  alias :__temp_to_s__ :to_s
  Bar.module_eval 'def to_s; super; end'
  super_to_s = to_s
  alias :to_s :__temp_to_s__
  super_to_s
end

end

puts Bar.new.to_s
puts Bar.new.foo_to_s

I’m just posting this because I asked about it a few weeks back and was told
it was impossible to call an overridden method in a base class. It isn’t.
I remember someone else who wanted to call the super of one method from
another but never figured out how to do it, and no solution was ever
presented, so here are a few ideas for him as well.

Chris

ruby-talk:38180

BTW, is it a bug in UnboundMethod#bind that the following doesn’t work?

class Base; def foo; puts “Base#foo”; end; end;
class Derived < Base; end
d = Derived.new
Base.instance_method(:foo).bind(d).call()

If I change line 6709 of eval.c from:

if (!((TYPE(data->oklass) == T_MODULE) ?
rb_obj_is_kind_of(recv, data->oklass) :
rb_obj_is_instance_of(recv, data->oklass))) {

to:

if(!rb_obj_is_kind_of(recv, data->oklass)) {

then the above works, and doesn’t appear to have any odd behavior.

</ruby-talk:38180>

?!? Why is it this way?? This seems intentionally complicated and
counter-intuitive.

This is the only thing I miss from C++: being able to specify “where a
method comes from” (using the scope operator in C++, but I don’t care how
to do it as long as I can do it).

Chris

  if(!rb_obj_is_kind_of(recv, data->oklass)) {

This is a stupid example, but with your modified version of ruby try

   a = Numeric.instance_method(:to_int)
   b = Method.instance_method(:call).bind(a)
   b.call

Guy Decoux

Hi,

···

In message “Re: How to call overridden methods (finally!)” on 03/01/03, “Chris Pine” nemo@hellotree.com writes:

?!? Why is it this way?? This seems intentionally complicated and
counter-intuitive.

Most of C implemented methods does not type check their receivers, so
without this way, they may crash by type mismatch (“type” here means
object structure type such as T_OBJECT). But recently I found the
fact this check might not be necessary. I still need to investigate
if it’s true. Stay tuned.

						matz.

This is a stupid example, but with your modified version of ruby try

a = Numeric.instance_method(:to_int)
b = Method.instance_method(:call).bind(a)
b.call

···

----- Original Message -----

I didn’t actually modify my version… never hacked the interpreter before.
That was Paul Brannan’s code, I think.

But, as your example certainly shouldn’t work (and really can’t work,
since it makes no sense… #to_int isn’t bound to anything!), one of two
things could happen: the interpreter raises an exception, or it crashes.

I’m assuming that it crashes in this case, because otherwise I don’t see any
problem at all. (It already raises an exception! Telling me that `to_int’
is unbound is a more helpful exception, anyway.)

Still, this code doesn’t crash the interpreter:

a = Numeric.instance_method(:to_int)
class UnboundMethod
def call
super
end
end

a.call

So if your example crashes, I see your point… sometimes it is
inappropriate to call a method from a parent class on a child classes
instance… but only very rarely! And when it is impossible, as in this
case, I have to ask:

Should UnboundMethod really be a child of Method?? If anything, it seems
like it should be the other way around (though the English (and Japanese?)
of it might falsely suggest otherwise).

And how is this related to classes only, and not to modules? I could design
a module with similar problems, but when it crashed we would call it a bug.
:slight_smile:

In any case, it seems to me that there would be relatively few places where
this would be a problem, and that they are easily fixable. Is this a design
decision, or a fear of implementation? It wouldn’t be too hard to make a
bunch of objects of the base and library classes and have them exhaustively
call all of thier ancestors’ methods.

I guess my ultimate question is this:

Does this feature exist because it is bad design to allow people to call
parent methods from child instances (in which case how do we justify
`super’?), or it is a good idea (like super), but it isn’t implemented
because it might cause crashes?

Chris

I'm assuming that it crashes in this case, because otherwise I don't see any
problem at all. (It already raises an exception! Telling me that `to_int'
is unbound is a more helpful exception, anyway.)

pigeon% cat b.rb
#!./ruby
a = Numeric.instance_method(:to_int)
b = Method.instance_method(:call).bind(a)
b.call
pigeon%

pigeon% b.rb
./b.rb:4:in `call': you cannot call unbound method; bind first (TypeError)
        from ./b.rb:4:in `call'
        from ./b.rb:4
pigeon%

Guy Decoux

pigeon% cat b.rb
#!./ruby
a = Numeric.instance_method(:to_int)
b = Method.instance_method(:call).bind(a)
b.call
pigeon%

pigeon% b.rb
/b.rb:4:in call': you cannot call unbound method; bind first (TypeError) from ./b.rb:4:incall’
from ./b.rb:4
pigeon%

Guy Decoux

···

----- Original Message -----
From: “ts” decoux@moulon.inra.fr


Thanks, Guy! You’re a lifesaver. :slight_smile:

So this seems more like an argument for allowing this into the standard
interpreter. Currently the above code raises the following exception
instead:

$ ./test.rb
./test.rb:13:in `bind’: bind argument must be an instance of Method
(TypeError)
from ./test.rb:13

Which I think is a less helpful exception. Is the problem that it is wrong
to call' something which is a kind_of Method, but not a Method instance? Or is the problem thata’ is an UnboundMethod?

Still, in the vase majority of cases, calling an overridden parent method
doesn’t raise an exception at all… it just does what you want it to do. I
think that in every case you can achieve the same results by using `super’
in clever ways, but it is messy and requires a lot of aliasing. Why not
allow us to do the same thing more cleanly?

Chris

/b.rb:4:in `call': you cannot call unbound method; bind first (TypeError)

[...]

Thanks, Guy! You're a lifesaver. :slight_smile:

How do you make the #bind in this case ?

Guy Decoux