Include vs. Extend, what's the difference?
What is the difference between include
vs. extend
in Ruby? And how do we know which one to use?
Note: the explanation below mentions singleton classes. For now think of a singleton class (aka metaclass or eigenclass) as a place where class methods are ‘held’. As opposed to instance methods which are ‘held’ in the class itself. All classes in Ruby have a singleton class. Not very clear, I know. Nor is a complete story. So I will make singleton classes a topic of a future post.
Let’s start with the motivation for include
and extend
.
Assuming we have a module Hello
with a ‘class method’ say_hello
:
?> module Hello
?> def self.say_hello
?> "hello"
?> end
>> end
We want to add this module’s functionality in our class Greeting
. Specifically we want the class method say_hello
. So we can just include
the module and that should do the trick right?
?> class Greeting
?> include Hello
>> end
>> Greeting.say_hello
(irb):28:in `<main>': undefined method `say_hello' for Greeting:Class (NoMethodError)
No. That’s doesn’t work. And here’s why.
When a class includes a module, it gets the module’s instance methods, not the class methods. The class methods stay tucked away, in the module’s singleton class.
What we have to do to add say_hello
as a class method to our target class Greeting
is to include the module Hello
in the class’s singleton class. To do this, we have to open Greeting's
singleton class like this:
?> module Hello
?> def say_hello
?> "hello"
?> end
>> end
?> class Greeting
?> class << self
?> include Hello
?> end
>> end
>> Greeting.say_hello
=> "hello"
This is some exotic syntax. But all that line with class << self
is doing is opening the singleton class of Greeting
.
Note that we also made say_hello
an instance method of the module (instead of a class method).
Why does this work?
When we include
the module in the target class’s singleton class, the module’s instance methods become instance method’s of the target class’s singleton class. This is good. Remember that singleton class is where the class keeps its class methods. So in our case, the module’s methods become class methods on the target class. Which is what we wanted.
What is extend
?
extend
is an instance method of Object
. Object#extend
is simply a shortcut that includes a module in the receiver’s singleton class. We can always do that manually as shown above, but the syntax for it, with the class << self
, is a bit awkward. So instead we can use extend
:
?> class Greeting
?> extend Hello
>> end
>> Greeting.say_hello
=> "hello"
Here are some facts about include
and extend
and how to think about when to use each:
include
mixes in the specified module’s instance methods as instance methods in the target class.include
is a private method ofModule
. It’s intended to be called from within a class/module definition.extend
adds the specified module’s methods to the target class’s singleton class. And as a result, as class methods on the target class.extend
is a public method ofObject
. And can be used at the ‘top level’.- In addition to classes, we can also
extend
objects in Ruby. If we callobj.extend(MyModule)
, nowobj
hasMyModule's
methods. But no other instance of the objectobj
’s class has those added methods. This is wild. Those added methods are called the object’s singleton methods and we can list them withobj.singleton_methods
- One more thing to know about
include
is thatinclude
adds the module to the target class’s ancestor chain, right above the class itself. We can see the list withMyClass.ancestors
.
To wrap up, we saw a number of different ways to answer the question “what’s the difference between include
and extend
?”.
The short answer is that include
adds instance methods, extend
adds class methods.
A more complete answer involves talking about the singleton classes and opening them up with the exotic class << self
syntax.
Note that neither include
or extend
are keywords though. They are both methods, like most things in Ruby.
Subscribe for more Ruby, Rails, and Hotwire
Short posts, clear explanations