Type errors in Ruby are very rare in my experience – I have about as
many in Ruby as I do in Java. Here are some reasons I think this is so:
-
If you name your variables well, it never occurs to you to pass the
wrong value to a method. -
The most likely causes of type errors are where you have a number of
variables of primitive types, or are using built-in containers, and you
pass the wrong one to a method. This is an indication that you should
refactor those variables into a higher-level abstraction, or that your
methods are too big. Constant refactoring helps avoid these kind of
errors. -
I don’t just unit test the postconditions of my classes. I also use
Mock Objects to test that they make correct calls to the other objects
that they collaborate with, and pass the expected values to those
objects. This catches errors described in point 2, above. -
When programming in Ruby you use polymorphism a lot. Static type
checking doesn’t help catch errors in polymorphic code. For example, an
interface definition in Java gives a name to a set of methods, but does
not specify how those methods are to be implemented – that has to be
specified in human-readable documentation. So if I implement those
methods incorrectly, the type checker does not catch the error.
Similarly for overriding methods in classes – if I implement equals but
not hashCode (or vice versa), the type checker does not catch the error,
even though I have broken the protocol defined by the Object class.
Therefore I have to write unit tests to catch errors in polymorphic
code.
For example, a common use of polymorphism in Ruby is to just implement
enough methods for an object to work when passed to some method. E.g.
an object may have a method that takes a collection as a parameter. If
that object only iterates over the collection by calling its “each”
method, I can write a compatible object that only implements “each”, and
ignore all the other methods provided by Ruby collections. If, later, I
the first object is changed to call other collection methods, the change
will be detected by the mock objects in the unit tests.
Compare this to Java where you’d have to implement the entire
java.util.Collection interface just to provide an implementation of the
iterator() method. This is especially difficult if your object does not
have a sensible implementation for many of the methods of an interface.
You have to then either stub those methods out to throw exceptions (like
immutable Java containers) or split the interface into an inheritance
hieararchy so that you can implement only the base interface. And then
when you find that the base interface is too fat, you have to split it
again, and again, and again…
Cheers,
Nat.
···
On Tue, 2002-08-27 at 21:09, Volkmann, Mark wrote:
It seems to me that having compile-time type checking
allows certain coding errors to be found more quickly and reduces the
possibility that users will experience the equivalent of a Java
ClassCastException or whatever happens in Ruby when you attempt to invoke a
method on an object that doesn’t support it.
–
Dr. Nathaniel Pryce, Technical Director, B13media Ltd.
Studio 3a, 22-24 Highbury Grove, London N5 2EA, UK
http://www.b13media.com