Yet another series attempt

I think my NubyGems series has gone over pretty well, so I figured I’d try my luck with something else.

This new series, digging deep, will be targeted towards the more experienced developer, or at least folks who want to know a little more about some deeper concepts in Ruby. I’m going to do what I can to make these as clear as possible, but I’m relying on input from gurus and skeptics to help improve these posts and make them a good resource for folks.

So this post will focus on a recent topic on RubyTalk, the merit of using Mixins as a suitable replacement for multiple inheritance.

Overview

If you haven’t worked with multiple inheritence before, or aren’t familiar with object oriented programming, this article isn’t going to be super helpful. However, as a quick review, the core concept of multiple inheritance is that an object can have more than one parent class. For example, perhaps you have a Student class, and you want to say Joe is a student, but he’s also a musician and a rubyist. Single inheritence would not let you have this kind of structure without awkward hackery, so multiple inheritence would make life easier.

Though I’m sure there are folks out there who can explain why Java’s interfaces help simplify things in some cases, this sort of thing is a notorious source of annoyance in Java. A lot of times single inheritance just doesn’t do the trick. So often when folks hear that Ruby is single inheritance, they get sort of nervous.

On the gripping hand, there are plenty of languages in which incorrect use of MI can really bite you. C++ and Perl would be common culprits, and I have to be honest, a ten foot pole isn’t long enough to keep me away from object oriented Perl 5.

Ruby I suppose is lucky enough to be able to happily steal good ideas and throw away the bad ones. With this in mind, I think the Mix-in idea really fits the language.

But isn’t a Module just a container / a crippled Class ?

A short definition of a Module would likely be ‘a ruby class that cannot be instantiated and does not live within the class hierarchy’. It wouldn’t be 100% complete, but it’s enough to work with.

This at first sounds inconsequential. But the semantic power that such a construct provides is rather vast. David Black mentioned that he likes the idea of having both a noun like construct as well as an adjective like construct available for modeling.

That’s is a pretty good way to put it. This object is an Array. It is Enumerable.

If you look closely, you’ll see the distinction. The relationship is not really an ‘is a’ for the latter, but more describes some behavior the object has. It’s noun vs. adjective, when it all boils down.

For a quick example of modules in action, have a look at this use of Comparable. To get all your usual comparision operators(<,==,>,<=,>=,!=) in ruby, you just need to implement one method, the comparison operator(or spaceship).

>> class A
>>   attr_accessor :data
>>   def <=>(other)
>>      data <=> other.data
>>   end
>>   include Comparable
>> end
=> A
>> a = A.new
=> #
>> a.data = 78
=> 78
>> b = A.new
=> #
>> b.data = 10
=> 10
>> a < b
=> false
>> a > b
=> true
>> b.data = 78
=> 78
>> a > b
=> false
>> a == b
=> true

Since Fixnums are Comparable, i can just delegate on down to get the right behavior. By including the Comparable module into my class, I get all those methods for free as promised.

This should be sufficient to show that we can avoid that interface problem. But it’s not going to tell you much about how mixins might save some headaches that MI introduce, so let’s get on to that, and luckily, this gives me an excuse to play with OmniGraffle.

But isn’t that just MI under a different name?

It might be tempting to just box off the idea of mix-ins as a direct comparison to multiple inheritance, but that’d be a misconception. The core difference between multiple inheritance and single inheritance is the former turns your hierarchy into a digraph where the latter preserves a tree structure. Since Ruby is single inheritance, the use of mix-ins prevents the issues associated with a more loosely arranged network of paths.

Python 2.3 has a very good implementation of multiple inheritance, and this is mostly due to the fact that if a cycle forms in the graph, it is considered illegal. (Which helps make method resolution sane). Please refer to this paper if you’re interested in the details, as they’re a little beyond the scope of this article.

However, this might be best explained graphically. I’m not a particularly graphical person, so bear with me :)

So the graphs on the left are meant to represent a situation where the leftmost path from D to A is the ‘main path’, and the other paths represent orthogonal concerns. (Think of them of doing things similar to Ruby’s Comparable or Enumerable). On the right hand side, you see the same idea, but the overlapping orbs represent modules ‘mixed in’ to D.

One of these clearly has a single linear path back to the root. The other would depend on how your language implemented its method resolution. Hopefully you can look at this and abstract the idea that no matter what, even when we mix modules into other modules, the results are a single component with no hierarchy attached to it. In this way, introducing new modules is much less likely to cause conflicts than introducing new parent classes, considering that it is rare in complex systems to know the entire topology well enough to avoid problems. I’m pretty sure this is the biggest case for the mix-in model, and it’s tough to find many situations in which its capabilities fall short of multiple inheritance.

I see the point, but I still don’t get how to use these things in my modeling

Basically, if you’re already familiar with modeling multiple inheritance in a sane way, the leap to mix-ins will be relatively small. However, if you’re new to the concept, it might take a little extra leap. Going back to David’s analogy, classes are our Noun component and modules our adjective.

Perhaps we have a Record construct and we want to be able to add tags to these records( a la folksonomy ). This is an ideal time to say an object with the ability to be tagged is Taggable, which is a nice adjective, and makes a nice module.

The following example is from Ruport’s source, and shows an actual use of modules:

module Ruport::Data
  module Taggable
    def tag(tag_name)
      tags << tag_name unless has_tag? tag_name
    end
    def delete_tag(tag_name)
      tags.delete tag_name
    end
    def has_tag?(tag_name)
      tags.include? tag_name
    end
    def tags
      @ruport_tags ||= []
    end
    def tags=(tags_list)
      @ruport_tags = tags_list
    end
  end
end

Nothing really surprising or fancy. However, both our Records and Tables are taggable, and they're not related to each other directly. This is what modules let us do, and it sure can be handy.

The light at the end of the tunnel

Hopefully this will bring folks who haven't been really thinking much about what modules are for closer to understanding how they work. Also, it might provide some insight for folks who are missing multiple inheritance, or for people who have been looking for a construct like this but didn't quite find it yet.

I hope this explanation has been helpful, but if you have questions, please do ask them. Also, if you have some suggestions on how to make this more clear or notice any mistakes, let me know as well. Hopefully future articles in this series will help expose some of the deeper concepts within Ruby to those who are interested!