How to copy a method from one class to another

S

Sam Kong

Hi Rubyists,

Is there a way to copy an instance method from one class to another?

class C1
def f
p self.class
end
end

class C2
end

Now I want C2 to have f method so that I can call C2.new.f.

Thanks.

Sam
 
L

Logan Capaldo

Hi Rubyists,

Is there a way to copy an instance method from one class to another?

class C1
def f
p self.class
end
end

class C2
end

Now I want C2 to have f method so that I can call C2.new.f.
You can inherit.

class C2 < C1
end

Or you can make C1 a module

module C1
def f
p self.class
end
end

class C2
include C1
end
 
S

Sam Kong

Sam said:
Hi Rubyists,

Is there a way to copy an instance method from one class to another?

class C1
def f
p self.class
end
end

class C2
end

Now I want C2 to have f method so that I can call C2.new.f.

OK. I need to describe what I want.
Actually this problem is rather imaginary than practical.
While solving some other problem, this question came into my mind.
Normally, I would use inheritance or mix-in or maybe delegation.
This question is "What if...?"

Let's say that there's a class (C1) in a library and I'm building my
own class hierarchy including C2 (class C2 < SomeOtherClass).
In such a case, Inheritance or mix-in is not an option.
However, I want to add a method of C1 into my C2.
As someone jokingly mentioned, I can copy the source code.
Let's assume that the method is implemented in C and we don't have the
source.

Can we still copy(or reuse) the method?

Sam
 
M

MonkeeSage

Sam said:
Let's say that there's a class (C1) in a library and I'm building my
own class hierarchy including C2 (class C2 < SomeOtherClass).
In such a case, Inheritance or mix-in is not an option.

Unless I misunderstand, a mixin is fine:

module C1
def f
p self.class
end
end

class C2; end

class C3 < C2
include C1
end

C3.new.f # => "C3"

Regards,
Jordan
 
S

Sam Kong

Hi Jeffrey,

Jeffrey said:
If the method only exists in a library class, how do you know what it
would do to your class? It sounds like the best way to get the effect
you want might be the same thing you would do in C++ or Java, viz. to
let C2 have a member of type C1, and forward method calls to the member
object according to the "Law" of Demeter.

Yes, I think you're entirely right on this.
If C2 doesn't know how C1#f is implemented, it's no used copying the
method's implementation or even copying the implementation won't work
in C2's context.
Now I know that my imaginary problem was totally non-sense.

Thank you for enlightening me.

Sam
 
F

Florian Frank

MonkeeSage said:
Unless I misunderstand, a mixin is fine:
This wouldn't work in the situation Sam describes:

class Rubyforge::C1 # for example in some rubyforge library
def interesting_method
if @foo > 3
@bar += 2
else
@baz /= 2
end
end
end

Sam wonders how to call interesting_method from his class C2, that
cannot inherit
from C1, because it already inherits from SomeOtherClass:

class C2 < SomeOtherClass
def my_method
interesting_method # ???
end
end

This isn't possible in Ruby, because of single inheritance and the fact
that Sam doesn't control the rubyforge library and thus cannot create a
module to include in C2. The only way to get interesting_method's
functionality into C2 is to copy & paste it.

If there was a way in Ruby to do that like:

class C2 < SomeOtherClass
include Rubyforge::C1.to_module:)interesting_method)

def my_method
interesting_method
end
end

This would introduce a tight coupling between C2 and C1 on the same
level like inheritance would do. In some way it would be equivalent to
real multiple inheritance. Now the author of C1 can break your code by
refactoring e. g.:

class Rubyforge::C1 # for example in some rubyforge library
def interesting_method
if @foo > 3
add_two
else
@baz /= 2
end
end

private

def add_two
@bar += 2
end
end

I was in the same situation and ended up copying & pasting. I wished at
the time, that it would have been possible to just import those methods,
even if it meant that I might ending up shooting myself into the foot.

There is an additional problem, that occurs when someone attempts to
import methods from a Ruby class implemented in C, that uses a macro
like RSTRING(foo)->len. In these cases it would be possible to trigger a
Ruby segmentation fault by calling those methods. Of course it would
perhaps be possible to mark C implemented methods and let Ruby refuse to
export them. This would at least cause an ordinary exception during
class loading time.
 
F

Florian Frank

Sam said:
If the method only exists in a library class, how do you know what it
would do to your class? It sounds like the best way to get the effect
you want might be the same thing you would do in C++ or Java, viz. to
let C2 have a member of type C1, and forward method calls to the member
object according to the "Law" of Demeter.
If this is possible, it is a better solution of course. I think in
general it's better to use delegation than to use inheritance for code
sharing purposes, using modules is maybe on par with delegation.
Yes, I think you're entirely right on this.
If C2 doesn't know how C1#f is implemented, it's no used copying the
method's implementation or even copying the implementation won't work
in C2's context.
Now I know that my imaginary problem was totally non-sense.

Thank you for enlightening me.
Ok, let me switch off the light again. ;) You would read the source code
before you do it and decide if you can prepare instance variables and
methods in the required way. I would call that "internal duck typing".
Of course this would make it necessary to study the source code of C1 to
find out what exactly it does. But this isn't so different from what you
have to do if you want to inherit from the class (especially in Ruby).
 
S

Sam Kong

Hi Florian,

Florian said:
This wouldn't work in the situation Sam describes:

class Rubyforge::C1 # for example in some rubyforge library
def interesting_method
if @foo > 3
@bar += 2
else
@baz /= 2
end
end
end

Sam wonders how to call interesting_method from his class C2, that
cannot inherit
from C1, because it already inherits from SomeOtherClass:

class C2 < SomeOtherClass
def my_method
interesting_method # ???
end
end

This isn't possible in Ruby, because of single inheritance and the fact
that Sam doesn't control the rubyforge library and thus cannot create a
module to include in C2. The only way to get interesting_method's
functionality into C2 is to copy & paste it.

If there was a way in Ruby to do that like:

class C2 < SomeOtherClass
include Rubyforge::C1.to_module:)interesting_method)

def my_method
interesting_method
end
end

This would introduce a tight coupling between C2 and C1 on the same
level like inheritance would do. In some way it would be equivalent to
real multiple inheritance. Now the author of C1 can break your code by
refactoring e. g.:

class Rubyforge::C1 # for example in some rubyforge library
def interesting_method
if @foo > 3
add_two
else
@baz /= 2
end
end

private

def add_two
@bar += 2
end
end

I was in the same situation and ended up copying & pasting. I wished at
the time, that it would have been possible to just import those methods,
even if it meant that I might ending up shooting myself into the foot.

There is an additional problem, that occurs when someone attempts to
import methods from a Ruby class implemented in C, that uses a macro
like RSTRING(foo)->len. In these cases it would be possible to trigger a
Ruby segmentation fault by calling those methods. Of course it would
perhaps be possible to mark C implemented methods and let Ruby refuse to
export them. This would at least cause an ordinary exception during
class loading time.

I think you're understanding my problem very correctly.
My need was sort of like multiple inheritance which is not supported in
Ruby.
Mix-in is the right way in Ruby.
However, for mix-in, all things should be designed before
implementation.
If you want to reuse some part of a class, there's no easy way and
probably it's not recommended like you said it's tight-coupling.

Thank you for your time and expertise.

Sam
 
R

Rick DeNatale

I think you're understanding my problem very correctly.
My need was sort of like multiple inheritance which is not supported in
Ruby.

And even in a language like C++ which supports multiple inheritance,
you can't pick and choose which functions you inherit.

In fact, it's even harder because, unlike a dynamic language like
Ruby, where inheritance and mix-in is used only to compose
implementation, in a strongly typed language they also are the
mechanism of type composition.

It's this tight-coupling of implementation and specification which
distinguishes the two approaches.

And it's one good reason why Ruby is NOT C++, nor is it Java, and one
of the reasons so many things seem to be easier in Ruby once you get
acclimatized.
Mix-in is the right way in Ruby.
However, for mix-in, all things should be designed before
implementation.

Not necessarily before, but in concert with, it's called iterative
design, the design evolves as you learn more about the problem by
implementing it.

Another difference in the strongly typed vs. dynamic languages is the
implications of refactoring. In a dynamic language it's more like
rearranging furniture, while in a strongly-type one it's more like
solving a 15-puzzle.
 
Y

Yukihiro Matsumoto

Hi,

In message "Re: How to copy a method from one class to another"

|Mix-in is the right way in Ruby.
|However, for mix-in, all things should be designed before
|implementation.

If you are not allowed to modify existing classes. Most of the cases
rewriting

class C1
def f
p self.class
end
end

to

module M1
def f
p self.class
end
end

class C1
include M1
end

class C2
include M1
end

is trivial. You don't need to design everything before
implementation, but just have to evolve classes.

If you are not allowed to modify pre-existing class, you need to use
some kind of delegation.

matz.
 

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,765
Messages
2,569,568
Members
45,042
Latest member
icassiem

Latest Threads

Top