Why Ruby’s Mixins Gives Rails an Advantage over Java Frameworks

Much has been made over Paul Graham’s famous posting about how Lisp gave his startup Viaweb an advantage over the competition.  Graham’s thesis is that there are features in the Lisp language that could be leveraged to make his programming team more productive and better able to respond to customer needs.

The idea that a programming language will make your team more productive is the Holy Grail of software development.  Many languages have been promoted as a cure all for productivity (C++ in the 80s, Java in the 90s, Ruby now, Lisp perpetually), and each turns out to have their benefits and weaknesses.  But there is no doubt that certain features in languages can lead to leaps in improvements in productivity.  For example, having built in garbage collection is a sine qua non for modern development languages.  No one can argue (at least no one can argue well) that garbage collection does not improve developer productivity for 99.9% of development efforts.

What is Mixin?

Since many readers may not be aware of what a mixin is, I will try to describe it briefly.  Ruby is an object-oriented language.  With all object-oriented languages, the designers had to decide whether or not to support multiple inheritance.  Perl and C++ designers allow for multiple inheritance, Java does not allow for multiple inheritance but does allow for polymorphism through the use of interfaces.  Ruby’s designers opted to not allow multiple inheritance, but to allow for code from one module to be able to be included in another class.

So consider the following code:

module BarModule
  def hello_world
    puts "Hello World"
  end
end

class BaseClass
  def class_method
    puts "In class method"
  end
end

class Foo < BaseClass
  include BarModule
end

f = Foo.new
f.class_method
f.hello_world

 In this example, we are creating a module named BarModule with a hello_world method, a class named BaseClass, and then another class Foo that extends BaseClass and includes BarModule. The class Foo will then have both the methods from BarModule and BaseClass, but it will only BE an instance of BaseClass as that is its only parent. Somewhat different than what a Java developer would do, but it makes sense. Running this file will result in the following output:

$ ruby foo.rb
In class method
Hello World
$

So that is a basic example of what a mixin is.

Adding send() into the equation

Another interesting and powerful thing about Ruby is that all method calls are actually message passing calls.  So for example, we could rewrite:

f = Foo.new
f.class_method
f.hello_world

to

f = Foo.send(:new)
f.send(:class_method)
f.send(:hello_world)

and the results would be exactly the same! Again a little weird if new to Ruby, but it this language feature can lead to some very interesting and powerful results.

Combining include and send

Now let’s combine these two methods.  What if I wanted to take my BarModule and apply it to a class that is not sign, say the ruby base class String.

String.send(:include, BarModule)
s = "Arbitrary String"
s.hello_world

Running the above code would produce:

$ ruby include-bar-module-on-string.rb
Hello World
$

That’s right, at runtime, I was able to add an arbitrary method onto the base String class. That method is now available to any Strings that I instantiate within my application.

The Implication of this Feature on Rails

Because of this mixin feature, a developer can add arbitrary methods and modify behavior of core classes at runtime.  This is amazingly powerful if you are trying to write plugins and extensions to the framework.  Because you can add functionality to existing objects, users can install your plugin and start taking advantage of new functionality without having to make changes to the objects that they are instantiating in their application.  In frameworks written in other languages, such as Java, plugging in new functionality means that you need to change how your objects are instantiated.  This will require code changes and/or potentially configuration changes (if you are using a dependency injection framework like Spring).  Hard to develop, hard to maintain, and a pain for plugin developers to support.  But because of the mixin feature, Rails plugin developers can customize the base objects and the users of the plugins do not have to change any of their code or configuration logic.  A good example of this can be seen in the Row Version Rails plugin.  This particular plugin puts a created_at, UPDATED_AT and a row_version on every row inserted into the database.  It requires ZERO code change to make this happen.  You just install the plugin and go.  It works by adding hooks into the ActiveRecord::Base (the base persistence class in Rails) so that when records are saved, the correct information is put in to those fields.  A very easy and powerful plugin to install and use.

Conclusions

The point to take away from this is not that Ruby on Rails rocks and Java sucks.  Far from it.  But choosing a framework with lots of extensions that can take care of many of the mundane tasks allows your developers to spend more of their time focused on the problems of the user and not on common problems.  The mixin feature of Ruby allows for the development of easy to use but powerful plugins that will be hard for any non-Ruby based framework to compete with.

22 thoughts on “Why Ruby’s Mixins Gives Rails an Advantage over Java Frameworks


  1. So many other languages support mixins, including Groovy.


  2. @Steven
    I really don’t know Groovy that well and it very well may be possible that Grails can benefit in the same way that Rails can from some of these features. Very similar, in Graham’s article, many languages have the ability to create macros. The point is that Java developers should consider that features of other languages may result in advantages for different frameworks.


  3. Interesting article.

    > …Java does not allow for multiple
    > inheritance but does allow for
    > polymorphism through the use of
    > interfaces.

    This is correct Java does not offer multiple inheritance. However, this does not have anything to do with polymorphism. Multiple inheritance means that one class can extend or inherit from more than one base class. Without multiple inheritance you can still have multiple classes all extending or inheriting from the same base class. This is what is required for polymorphism.

    I am currently trying to learn Ruby. I was wondering if you could explain any of the side effects of mixins. For example, do mixins have access to the member variables of an object that they are “mixing in to?” Is there a way to protect against this?

    Thanks.


  4. @Mike

    Polymorphism in Java comes in also through the use of interfaces. Although a class can inherit from only one class, it can implement n-interfaces, so the class ArrayList is an AbstractList and a Cloneable and a Serializable, etc.

    To the second point, IMNARE (I am not a Ruby expert), but mixins do have access to internal attributes of the object as well as protected methods in the class.


  5. As a Java developer I don’t understand why do you enjoy broken encapsulation and multiple inheritance? Such ability to “extend” any class actually scares me, because it leads to chaos and bad design.


  6. @Andreas
    The tapestry mixins are a very interesting implementation, but not nearly as powerful as Ruby mixins. In Ruby, mixins are a component of the language, so every object can have its behavior modified. With Tapestry, it is just certain components that can fit the paradigm.

    I do not know Tapestry well and have never used the mixins there. If I specify a mixin annotatiion, does the injected object have all of the mixin methods available to me as well as all of the methods of the base component? Or is it just changing behavior of how the injected class works on Tapestry lifecycle methods?


  7. @Ivan

    Mixins are a tool. Like any powerful tool, when used incorrectly, you can cut yourself. But when used correctly, it can empower developers to create better systems.

    Again, read the article by Paul Graham. Lisp is an extremely powerful language, but you can definitely hurt yourself with it. However, there can be an advantage for a good development team in using such a sharp tool as they can improve their product faster than a team using simpler tools.


  8. @Rob

    The T5 mixins are being done using bytecode enhancements and such so it is “very close” to the ruby idea of mixins (which inspired the feature) – only very specific to component development which you have pointed out. (of course in Tapestry everything is a component)

    I would so f-ing love if java could have mix-ins. It’s impossible and it can’t as it’s so totally a dynamic language kind of thing but man that would be so f-ing nice. Maybe someone really really smart will find a way to add it to the language in some broken form somehow.

    p.s. Long time no see, good blog entry.


  9. And with great power comes great responsibility

    — fires web over evil doers —

    The mixin is very powerful but, as with Objective-c, the runtime modification of classes, either via replacement or addition, can lead to confusion as to how certain functionality is achieved.

    Initially the flexibility of said features can be intoxicating but abuse can be problematic.

    Interestingly, it is exactly this condition that lead Jane St Capital to adopt a functional programming language (OCAML), which allows them to determine behaviour explicitly without tracking n modules.

    I am not saying that it is not a powerful feature or that you should not use it but some caution is needed.


  10. @Jesse and @Rob (I guess)

    “””
    I would so f-ing love if java could have mix-ins. It’s impossible and it can’t as it’s so totally a dynamic language kind of thing but man that would be so f-ing nice
    “””

    How would you compare Scala’s traits w/ Ruby mixins. I feel like it’s rather similar, and Scala isn’t dynamic (in the “typing” way, which is I think is the way you are using it here).

    Scala Traits:
    http://www.scala-lang.org/node/126


  11. The problem with the Java folks is that they point out to scala and groovy, but ultimately fail to admit that Java does not allow anything like adding behaviour as easily as Ruby does.

    SomeClass.send :include, NameOfModule

    That’s it. One line of ruby. Screw the “with great power comes great responsibility”. If this simple feature confuses you, perhaps you should stick to the great world of Java.

    It is a bit strange to compare Ruby to Java though – comparing it to groovy, scala or python may be a lot better, because Java is very different.


  12. The one thing Ruby and Rails folks fail to take into account is the complexity that mixins add when the shit hits the fan. Trying to backtrace a problem through a complex Rails application when some automagic in a mixin has failed is quite possibly the most painful programming experience EVERRRRRR!! And I’m comparing this to memory leaks in C++.

Comments are closed.