In the first part, we learned that a module was a namespace, a way to regroup similar things to help us organize our application better and to avoid name clashes. It was rather easy to understand because we didn’t speak about the true power that resides in modules.
Other than a namespace, what is a module?
A module is like a class with 3 key differences :
- It is not a class (… DUH!)
- It cannot be instantiated (i.e. you cannot do x = MyModule.new)
- It can be mixed in a class to enhance its possibilities
And here is the tricky part : Although a module cannot have instances, it can still have instance methods. How is it possible? Well, you guessed it : those instance methods will become the instance methods of the client (the class).
That is what we call a mixin!
Mixins, quite a funny word don’t you think? No, you’re absolutely right, it is not funny at all. Mixin just means Mixed in, as in : This module has been mixed in my class, let’s play Twister!
When you decide to mix a module in one of your class, the latter automatically gains some new functionalities… for free! In a sense, it’s a little bit like class inheritance, at the exception of these 2 points :
- There is no hierarchy : the class isn’t the child of the module, it just includes it. You just have to see the mixed-in module as a set of additional methods for your class.
- You can include more than one module in a class, thus creating the effect of multiple inheritance… which is something most languages simply don’t allow
A little example to illustrate my first point :
Class inheritance : An aloes (class) IS A plant (class)
Mixins : An aloes (class) HAS some healing properties (module)
If we take for granted that our module “HealingProperties” is general enough in its implementation to fit many purposes, we could very well mix it in any other class that needs healing properties. (a drug, Wolverine, Kenny in South Park, etc). This is how we would do it :
module HealingProperties def heal_wounds #do something that heal wounds end end class Wolverine include HealingProperties def kill_something_with_my_claws! #Arrr! end end logan = Wolverine.new logan.kill_something_with_my_claws! logan.heal_wounds
The Comparable module
The built-in module called Comparable is possibly one of the best example to use to understand the usefulness of modules because it contains general purpose methods that can interest different kind of classes.
Comparable contains the following methods (yes, these are all methods, not operators) :
<, <=, ==, >, >=, between?
What are those methods doing exactly? Well, they compare sortable things together. However, those “things” to compare have to be in the class that includes the module, not in the module itself. How can the module knows what the “client” class is all about? The answer lies in the secret treaty that exists between Comparable and any class that wants to use it. The class has to specify, by implementing the <=> method, the attribute on which the comparison must operate. Note that <=> is a comparison method that is always expected to return +1 if the receiver is higher than the method argument, -1 if the method argument is higher then the receiver and 0 if both the receiver and the method argument are equal. In case you’re confused, if I write x <=> y, x is the receiver and y is the method argument.
class Dog include Comparable attr_accessor :iq def initialize(iq) self.iq = iq end #Honoring the contract with Comparable... def <=>(other) self.iq <=> other.iq end end spike = Dog.new(60) rex = Dog.new(40) retard = Dog.new(30) dumbass = Dog.new(20) dumber = Dog.new(15) bozo = Dog.new(10) puts "Make fun of dumbass" if spike > dumbass puts "Make fun of dumbass even more" if dumbass <= rex puts "Realize in shame that dumbass is not a dog but an African violet" if dumbass < bozo
In this example, I told Comparable that the comparison had to operate on the iq attribute. Doing so, my Dog class gained access to 6 comparison methods without me having to write a single line of code.
A little clarification about <=>
This hasn't much to do with mixins, but it may be confusing at first : Why do we used <=> once more in the implementation of our <=> method? Wouldn't it create some kind of infinite loop? No... because when we do self.iq <=> other.iq, we call the <=> method that resides in the Fixnum class (since iq is a fixnum). The <=> implementation in the Fixnum class already returns -1, 0 or +1 based on which one of the 2 fixnums is the highest. If iq would have been something else that DOESN'T implement the <=> method, we would have been forced to implement the <=> ourselves (e.g. return 0 if this_condition; return -1 if this_other_condition; return 1 if yet_another_condition)
It's modules like Comparable and Enumerable that makes the concept of modules so attracting. Yes, modules can be used as namespaces only, but they really shine when you use them to enhance your classes. They become especially powerful when they are consisted of general purpose methods and when they only require a small piece of information from the class to work properly.
23 thoughts on “An introduction to modules : Part 2”
Hey, that was wonderful! Thank you 🙂
So if I understand this correctly, it’s almost like a “preimplemented” interface… Interesting! Finally a plus for Ruby over dotnet! 😉
Exactly. I find the term “preimplemented” interface pretty accurate in fact! Thanks
Hey, really cool. Thank you. 🙂
“Finally a plus for Ruby over dotnet!”
Alas….I understand Ruby Modules …Thanks
Thanks so much for such a clear and understandable explanation!
thanks Frank, got it. but what’s about the ‘instance variables’ in module??
wow … that’s not my avatar..
Thanks guys, I’m glad you liked the post!
freenight,this is a very good question.
Instance variables in modules can belong to either :
1: The module
2: The client class
If you need a variable that will exist at “module level” you will do it this way
#this method is not accessible for a client class
#in this case the term “instance” refers to the module itself
@x = “bla bla bla”
SomeModule.get_instance_variable #output : bla bla bla
Conclusion : When you use instance variables inside “module level” methods, these variables are only available in the module itself.
Now, if you want to use instance variables where an “instance” refers to the client class instance and NOT the module itself, you have to proceed this way.
#only difference is that I removed the ‘self.’ part in the method definition. It means that this
#method will reside into the client class instance and NOT in the module itself
@x = “bla bla bla”
x.get_instance_variable #output is “bla bla bla”
got it Frank.
the first form:
@x = ..
since module themselves are object, classes that get this module included have no access to it’s instance method, thus this @x is visible only in the module itself.
on the other hand, instance methods defined in modules becomes instance methods of object with which the class has got the module mixed in, thus this instance variable is.. well instance variable in
per-object basis,right Frank??
sorry for my pool english..
I’ll try to sum it up in a more concise way :
When you are in a module and define a “module method” (e.g. def self.my_module_method), the execution context is the module itself (which is an object as well). When you are in a module and define an “instance method” (e.g. def my_instance_method), the execution context is the class instance that includes the module. This is like you had pasted all the instance methods from the module in your class. This is why we can say that the instance variables are in the class
wow ,wonderful explanation of module and mixin . i enjoyed reading it. i learnt d concept very easily by your explanation.
Congrats! Great article. I really enjoyed this blog. Bookmarked!
Actually, not quite correct… including a module inserts that module in the class inheritance hierarchy:
module M; end
class Fixnum; include M; end
=> [Fixnum, M, Integer, Precision, Numeric, Comparable, Object, Kernel]
Knowing this better explains why you get the methods of a mixin, but no direct access to it’s instance variables.
I am new to Ruby, but not that new to reading tech. posts. This one is funny and really concise! thanks 😉
Great site. A lot of useful information here. I am sending it to some friends ans also sharing
in delicious. And obviously, thank you in your effort!
Hello, i think that i noticed you visited my site
so i came to go back the prefer?.I am attempting to in finding issues to improve my website!I suppose its ok to make
use of a few of your concepts!!
Can you tell us more about this? I’d want to
find out some additional information.
Aw, this was a very nice post. Taking the time and actual effort to create a great article… but what can I
say… I hesitate a lot and never manage to get nearly
I was going nuts trying to figure out what Comparable methods do and how to code/implement them. After reading through countless books and online sources, this blog post is the only one that explained it in terms that a complete beginner like me would understand. Thanks so much for this very informative and valuable post on the comparable module.