Suppose I have a module called Flight
with both class and instance methods. I can get its methods into a class using include
, extend
, or both:
class Bat < Mammal
# Add Flight's class methods to Bat.
extend Flight
# Add Flight's instance methods to Bat.
include Flight
...
end
include
will add Flight
to Bat.ancestors
, but extend
will not.
My question is, why is this different for modules than for classes? When I subclass Mammal
, I always get both class and instance methods at once. However, when I mix in a module, I cannot get both class and instance methods at once (unless I use the self.included
hook or something like ActiveSupport::Concern).
Is there a language-design issue behind this difference?
I would like to address one part of your question:
include
will addFlight
toBat.ancestors
, butextend
will not.
extend is not the same as include so it does something different obviously... You can think of extend being equal to an include on the class' metaclass.
Have a look at the following example:
module M
end
class A
include M
end
# then you will see M within A's ancestors as you know
A.ancestors # => [A, M, Object...]
class B
# the following is roughly the same as extend M:
class <<self
include M
end
end
# then you will see M within B's metaclass' ancestors
MetaclassOfB = class <<B; self; end
MetaclassOfB.ancestors # => [M, Class, Module...]
So, since extend is like an include on the metaclass, you see the extended modules showing up in the metaclass' ancestor chain...
Both Module#include
and Object#extend
are used to add the instance methods of a Module
to an Object
.
Given the module:
module Flight
def can_fly?
true
end
end
Module#include
is used to add (or mix in) the instance methods of a module to the instance methods of a class or a module:
class Bat < Mammal
include Flight
end
a = Bat.new()
a.can_fly? # true
It actually affects the Object#is_a?
method, so:
a.is_a? Flight # true
Module#include
is a private method, so it can only be called with function notation when defining a class or another module:
class Bat < Mammal
self.include Flight # NoMethodError: private method called
end
Object#extend
adds the instance methods of a module as singleton methods to the object on which it's called, so you can do this:
b = Mammal.new()
b.extend Flight
b.can_fly? # true
b.is_a? Flight # true
c = Mammal.new()
c.can_fly? # NoMethodError: undefined method
And only b
will have the instance methods from Flight
; other Mammal
objects won't.
When calling Object#extend
inside a class definition, the methods are added to the eigenclass of the class you're defining.
This is the important difference between the two methods when using them inside a class definition, because the methods are added as class methods:
class Bat < Mammal
extend Flight
end
Bat.can_fly? # true
d = Bat.new
d.can_fly? # NoMethodError: undefined method
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With