Interfaces in Java and .NET are exactly like legal contracts (where the compiler is the all-powerful judge). If you implement an interface yet don't meet the terms of the contract, the judge refuses to allow you to proceed.
Mixins in Ruby are more like promises than contracts. When you agree to pick up a friend at the airport, you don't sign a contract, you promise you'll be there. The punishment is purely social: if you break a promise, your friends won't do stuff for you. Here's an example.
In Ruby, a common Mixin is
Comparable, which gives you the same behaviors as the
Comparatorinterface in Java. The promise in
Comparableis that you will implement the "spaceship" operator (<=>), which returns negative if the left-hand side is less than the right, 0 if they are equal, and positive if the LHS is greater than the RHS (which is exactly like the similar
compare()method in Java).
attr_accessor :name, :salary
def initialize(name, salary)
@name, @salary = name, salary
name <=> other.name
list = Array.new
list << Employee.new("Monty", 10000)
list << Employee.new("Homer", 50000)
list << Employee.new("Bart", 5000)
# Monty vs. Homer
list < list # => true
# Homer vs. Monty
list > list # => false
# Homer is between Bart and Monty?
list.between?(list, list) # => true
If you violate the promise by mixing in
Comparableand yet don't implement the spaceship, nothing bad happens...until you try to ask your friend
Comparableto perform a service for you (like compare two things with
between?or call the
sort!method). Then, you get an error message complaining "undefined method `<=>' for Employee". And this happens at runtime, not at compile time.
This is a fundamentally different mindset than the legalistic strongly contracted languages. It demonstrates one of the reasons that you can't slack off on writing your unit tests in Ruby: you don't find out important things until runtime. It is certainly a different way of thinking about implementing APIs. While lots of Java developers think this leads to chaos in large development teams, I have not found that to be true (and I think that most Ruby developers would agree with my assessment). The trade-off is strict control vs. flexibility. If there is one thing that my experience has taught, it is that flexibility is the most important thing for strong programmers, echoed by many of Paul Graham's essays.
If you must have stronger contracts in Ruby, there is a framework called Handshake that allows you to create pre- and post-conditional invariants ala Eiffel. While I understand the appeal of Handshake, I would be loathe to use it on a project because I prefer the power of flexibility rather than the strictures of constraints.