From: Paul Brannan Date: 2002-09-24T04:33:10+09:00 Subject: Re: The problem with run-time type checking On Tue, Sep 24, 2002 at 01:38:02AM +0900, Brian Denny wrote: > if "length" were part of the Enumerable interface, i would consider the > above behavior to be a defect in the library. but it's not. the point is > that i, as the user of a library, can expect to be able to make certain > assumptions when i know that an object is of a certain type, as defined by a > class or an interface/module, that i cannot make if i just know that they > both respond_to? the same symbols. Enumerable is a mixin, not an interface. It's used to add additional methods to a class that already has an each() method. Because a class includes Enumerable doesn't mean that an instance of that class will have all the methods defined in Enumerable (I can remove or undefine them), nor does it mean that a class that doesn't include Enumerable lacks an each_with_index() method that has the same semantics as the one in Enumerable. In Ruby, an object's class just isn't a very good measure of an object's type. As you pointed out, the set of method names an object responds to is also not a good measure of the object's type, because it's possible to have to objects that both respond to the same method, but to give the method different semantics for each object. This is why using respond_to? to check an object's "type" is frowned upon. I know of no good way to check for the semantics of an object's methods except by calling those methods; this is where unit tests come in. An alternative is to use modules as if they were interfaces, but I have not yet seen an implementation that does this well. I'm also not sure it would be widely accepted in the Ruby community. In the case of String#length and String#each, the problem is that each iterates over the lines in the String, while length tells you how many characters are in the String. IMO, this is a bug in String's interface; as a result, I rarely call String#each directly, but instead call String#each_byte or String#each_line. Paul