On Wed, 2003-11-19 at 10:30, Weirich, James wrote: > David Black (dblack / wobblini.net) wrote: > > Class name checking doesn't ensure needed behavior. Actually, let me > > I share some of David's concerns, but would like to point out that not only > does Class name checking not ensure needed behavior (which is true, but a > small danger in my mind), it overly constrains the software to reject > perfectly good solutions. > > Consider the following function. > > def read(io_object) > fail "Not an IO Object" unless IO === io_object > io_object.read > end > > This seems like a perfectly reasonable function and we feel safe because > carefully check the "type" (actually Class ancestry) of our parameter. > > Now consider the following usage: > > io = StringIO.new("HI") > read(io) # => RuntimeError: Not an IO object > > This perfectly reasonable use of read will fail because StringIO does not > enherit from IO, even though it implements IO-like methods. This is a > shame. Our "type-checking" has needlessly constrainted our solution. I think this is a flaw in the current ruby libraries, not a flaw in the concept of typechecking. There can be an interface defining what it means to "be an IO". Any object which does in fact behave exactly in accordance with this contract should mark itself as being an implementation of that interface. I suggest this approach: # an IO object fulfils the following contract: # ... details here ... module IO; end # note: no implementation!!! class StringIO # declare that this class fulfils the IO contract include IO ... end class File # declare that this class fulfils the IO contract include IO ... end Now someobj.is_a?(IO) will return true for all those objects which fulfil the IO contract. Note that the IO module does *not* have any implementation; it is purely an abstract concept and therefore can be mixed in to any existing class. When mixed in, it functions as a promise of behaviour which can be checked for at runtime. I expect that some people will put forward the idea that some class might be invented that happens to also fulfil the IO contract without being explicitly marked so, and that checking for the "IO" marker would disallow passing this class. Well I think the chances of a class happening to exactly fulfil a contract without that contract ever have being taken into consideration is smaller than the chance of us all being wiped out by an asteroid. Note that checking for *concrete* classes using is_a is a totally different issue. That does present problems regarding the inheritance tree, because you can't just mix in such a type to any other type. I think the arguments against this pattern are on much stronger ground. Comments? Simon