From: Ben Giddings Date: 2003-08-09T04:14:36+09:00 Subject: Re: Ducktype, right? On Fri August 8 2003 2:36 pm, Chris Morris wrote: > Ben Giddings wrote: > I agree with Ryan's reply to you. If I only need the one method on the > object I'm interacting with -- should that one call not be allowed > simply because all of the other methods that I don't need aren't > consistent with what it *should* be? Ouch, this is one of the most difficult-to-understand sentences I've seen in a long time. I'll pretend I understand it and give an example: # # Get the contents of the object, if it acts like a string, # simply return it, if it acts like a file, return its contents, # if it acts like a frobnitz, foozle it. # def getContents(obj) if obj.respond_to? :read return obj.read elsif obj.respond_to? :to_str return obj.to_str elsif obj.respond_to? :foozle return obj.foozle end end class EmailMessageBody < String attr_accessor :read def initialize @read = false # By default, mark the message body as unread end end Imagine that the "getContents" method is buried deeply within the source code to a library somewhere. The programmer would never see that method, even if he/she did see the commenting of the method. Now what happens when I pass an EmailMessageBody into getContents? It will see that it responds to "read" and return a boolean. That's not what you want. That's not what the documentation says, but that's what it will do. This could be a really hard bug to track down. Testing to see if the "read" method is supported is not really "duck typing", in the sense that you're not really checking to see if it's a duck. Just because the object implements a certain method doesn't mean that that method does what you expect. In certain cases, checking to see if a method exists is enough. The method "to_str" is a prime example. If someone implements that method in a way that doesn't return a string, they deserve whatever problems they get. There are plenty of examples of how to_str is used everywhere else, and the function name is pretty descriptive. Other functions are less obvious. Somebody else gave this example (though I added the "implements Duck" that I think was just accidentally left out): interface Duck { void quack(); } class MyDuck implements Duck { void quack() { System.exit(); } } Here the "implements Duck" is essentially an agreement saying "I want other peole to see this as a Duck". You're free to break that agreement, but if you do, you can expect trouble. On the other hand: class Psychiatrist { void quack() { quack_references++; } void shrink() { shrink_references++; } } Just because someone has a "quack" function/method, doesn't mean they want their object to be treated as a Duck. When you create a class and add methods to it, should you always have to say: "Hmm, what's the most popular way _____ is used", and make sure yours acts the same way? My understanding was always that "implements Foo" in Java meant: "my class will define the following methods, in the way that Foo describes, so you can treat my class as a Foo". As for the subject of why it is useful to implement a set of methods rather than just the one you care about, consider a function like: def dumpContents(obj) if obj.respond_to? :write obj.write(data) obj.flush if obj.respond_to? :flush obj.close if obj.respond_to? :close elsif ... end end I think it would be much more clear if you could simply say: def dumpContents(obj) if obj.implements_interface? :WriteableIO obj.write(data) obj.flush obj.close elsif ... end end Ben