An introduction to modules : Part 2

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 :

  1. It is not a class (… DUH!)
  2. It cannot be instantiated (i.e. you cannot do x = MyModule.new)
  3. 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 :

  1. 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.
  2. 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

  1. So if I understand this correctly, it’s almost like a “preimplemented” interface… Interesting! Finally a plus for Ruby over dotnet! 😉

  2. 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
    module SomeModule
    def self.set_instance_variable
    #this method is not accessible for a client class
    #in this case the term “instance” refers to the module itself
    @x = “bla bla bla”
    end
    def self.get_instance_variable
    @x
    end
    end
    SomeModule.set_instance_variable
    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.
    module SomeModule
    def set_instance_variable
    #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”
    end
    def get_instance_variable
    @x
    end
    end
    class SomeClass
    include SomeModule
    end
    x=SomeClass.new
    x.set_instance_variable
    x.get_instance_variable #output is “bla bla bla”

  3. got it Frank.
    the first form:
    module SomeModule
    def self.talk
    @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..

  4. freenight, Exactly.
    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

  5. wow ,wonderful explanation of module and mixin . i enjoyed reading it. i learnt d concept very easily by your explanation.

  6. Actually, not quite correct… including a module inserts that module in the class inheritance hierarchy:
    module M; end
    class Fixnum; include M; end
    2.class.ancestors
    => [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.

  7. 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!!

  8. 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
    anything done.

  9. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *