Chicken Typing Isn't Duck Typing
Posted by Rick DeNatale Mon, 22 Oct 2007 14:43:00 GMT
Trans just posted a question on ruby-talk.It seems that he uncovered a conflict between the facet's library and the Ruby standard open_uri library. Open-uri defines a module called Meta which it uses to extend certain StringIo instances. The Meta module adds several methods including one called meta, which returns a hash of meta information. In two places in open_uri, the message "respond_to? :meta" is used to test whether a particular StringIO instance has been extended.
The problem is that Facets also defines a method in either Object or Kernel called meta which provides a clever way to access the singleton class of an object. So the respond_to? in open_uri was getting false positives.
Trans asked "So where lies the fault in this conflict? Are extensions the bad guy, or is respond_to? really not a good oop concept? Or..?"
David Black replied that he thinks "this is just the age-old issue about adding methods to existing classes and hitting conflicts. #respond_to? is relatively dumb; it can't tell you what the method actually does. I think of it as an important tool for "soft" duck typing, in contrast with "hard" duck typing where you just send the object the message. "
Reading this made me realize that a much better name than "soft" duck typing, for using kind_of?/is_a?, respond_to?, or other such "sniff first" techniques, is Chicken Typing
Chicken Typing Isn't The Dynamic Equivalent of Static Typing
From what I've observed chicken typing is a "safety blanket" technique used by programmers experienced in statically typed languages, who seem uncomfortable with making the paradigm shift needed to really make dynamically typed languages work.
But because Ruby is dynamically typed, such techniques work imperfectly at best. Here are some of the problems:
- Checking with respond_to? can give false positives like this because just because an object has a method with the name you are looking for, doesn't mean that the method has the semantics you expect
- Similarly respond_to? can give false negatives when methods are implemented dynamically via method_missing. Chicken typers try to enforce conventions which require overriding respond_to? to get around this, but in general this is difficult, expensive, or impossible to do perfectly.
- Checking with kind_of?/is_a? is unnecessarily constraining. In Ruby you don't need the the equivalent to Java Interfaces or Abstract Classes, and once you've really made the paradigm shift from chicken typing to duck typing, you realize that you don't want them.
What's OpenURI Doing?
Because of Trans' question, I decided to look at how OpenURI was using respond_to? in this case. Here's the code in question:
First the beginnings of the OpenURI::Meta modulemodule OpenURI
module Meta
def Meta.init(obj, src=nil) # :nodoc:
obj.extend Meta
obj.instance_eval {
@base_uri = nil
@meta = {}
}
if src
obj.status = src.status
obj.base_uri = src.base_uri
src.meta.each {|name, value|
obj.meta_add_field(name, value)
}
end
end
attr_accessor :status
attr_accessor :base_uri
# returns a Hash which represents header fields.
# The Hash keys are downcased for canonicalization.
attr_reader :meta
# MORE STUFF....
end Meta.init causes the object passed as the first parameter to extend Meta, if a second parameter is passed, it's presumably also a Meta, and it's meta attributes are copied to obj.
Now here's the use of chicken typing:
module OpenURI
#...
class Buffer # :nodoc:
def initialize
@io = StringIO.new
@size = 0
end
attr_reader :size
StringMax = 10240
def <<(str)
@io << str
@size += str.length
if StringIO === @io && StringMax < @size
require 'tempfile'
io = Tempfile.new('open-uri')
io.binmode
Meta.init io, @io if @io.respond_to? :meta
io << @io.string
@io = io
end
end
def io
Meta.init @io unless @io.respond_to? :meta
@io
end
end A few observations:
- The variable which is being chicken typed, in both cases, is an instance variable which is set by the initialize method and never rebound. So there's no concern about checking an object which was handed in, e.g. through a parameter. We might be concerned that someone outside the class might somehow access it and change it to extend Meta without the Buffer knowing about it, but...
- The instance variable will always extend Meta after the accessor is called, so the above concern is moot.
- In the io accessor method, respond_to? is used to prevent re-extending Meta. This could be avoided by either extending the object referred to by @io in the initialize method, or, if there's a legitimate reason to defer the extension, either adding a state instance variable to indicate that the extension has been done, or just change "unless @io.respond_to? :meta" to "unless @io.kind_of?(Meta)". This is one case where kind_of? is really useful since we're really just checking if code under our control has extended an object under our control, with a particular Module.
- Finally, a minor point, but I find this code unnecessarily hard to read because of the choice of name 'io for the temporary in the <.<. method. Look at the line "io.binmode". Which io are we talking about? Is it the io we just assigned, or the result of the io accessor method. Now it happens to be the former, but knowing that requires knowing that the assignment above is what causes the Ruby parser to treat it as a temporary rather than a method send.
Better a Duck Than a Chicken
So in my opinion, it's better to be a bold Duck than a timid Chicken. Chicken typing is weak, and it really doesn't provide much more than the "warm fuzzies". Duck type, and build your specs/tests as you code to provide long-term robustness in dynamically typed languages.










Good article. Glad you took the ruby-talk conversation a little more in-depth.
I think you’re a little too hard on is_a?/kind_of? though. I don’t think they fit the “chicken” typing character quite as well. Yes, they aren’t 100% in a dynamic language such as Ruby’s, however they are much much more dependable than respond_to? b/c modules and class intend to represent an encapsulation of behavior. If we ask ‘is_a? Enumberable’, we can be pretty sure that we are working with something that responds to #select or what have you as the way we’d expect.
I absolutely agree with your conclusion though. One should really question the necessity of using any of these methods—especially respond_to?
I’m actually coming to believe that #respond_to? should never be used. Period. It seems to me I can’t recall a single time #respond_to? didn’t turn some sort of problem.
P.S. A few typos in there—like the first word ;)
Trans,
First off, I’ve fixed the misspelling.
True enough, but I wouldn’t say much more dependable. Even within an implementation hierarchy, the signature and even the meaning of a method can change. Consider, for example how the details of the each method vary depending on whether the enumerable is, say, a Hash, vs. an Array. You’re typically expecting certain things to be yielded to the block by each, and neither kind_of? or respond_to? helps predict either the arity for the block expected, or what will be yielded.
And the flip side argument against chicken typing with kind_of? is the false negative problem. There are interesting cases where unrelated classes implement similar methods or families of methods.
And sometimes the method similarity can be complex, making determining substitutability difficult. Consider how many classes implement some form of [], and how many variations of parameters there are affecting the semantics, even within the same class.
While some of the chicken typing persuasion might look at this as a mess needing to be cleaned up, duck typers see it as the powerful flexibility that gives Ruby it’s agility when coupled with the right approach and tools.
I’m not sure I invented the term, but I definitely agree with your sentiment (and love the term).
Ruby could help out more (for example, by tracking the provenance of changes), but even without that help, I’m happy to suffer the occasional minutes of confusion in exchange for the years of flexibility).
Dave
Hi Rick,
could you explain how the problem can be solved with DuckTyping when two libraries extend classes or objects with the same methods?
Thanks -stephan
“There are interesting cases where unrelated classes implement similar methods or families of methods.”
Death by delegation!