Ducks Can Be Subtle Birds
Posted by Rick DeNatale Thu, 10 Aug 2006 15:09:00 GMT
One theory I’ve seen defines a “duck type” as a set of messages which an object bound to a parameter or value needs to understand. This leads some, who want to make type-checking happen a bit earler, to propose testing the values of such variables with oneor more respond_to? tests before using the object “in anger.”
But ducks can be subtle…
Sometimes it’s not enough just to have a certain set of methods in a duck’s repetoire.
Let’s look at an, admittedly cooked-up, example:def reverse_lookup(value, dictionary)
(dictionary.collect { |kv| kv[1] == value ? kv[0] : nil }).compact
endirb(main):001:0> load 'reverse_lookup.rb'
=> true
irb(main):002:0> reverse_lookup(1, { 'a' => 1, 'b' => 2, 'c' => 1})
=> ["a", "c"]The point here is that for this implementation of reverse_lookup, the only message sent to dictionary is collect, soaccording to the “respond_to?” theory any object which has “collect” in it’s repetoire is the right species of duck for the dictionary parameter.
So let’s try another duck:irb(main):003:0> reverse_lookup(1, [1, 2, 3])
=> [0, 1]Well, it works in the sense that nothing blows up, but the results look strange, where did that 0 come from?
While you weren’t looking, I snuck a debug parameter into my reverse_lookup method to give an “x-ray” view into what’s happening.irb(main):004:0> reverse_lookup(1, [1, 2, 3], true)
kv = 1, kv[0]=1, kv[1]=0
kv = 2, kv[0]=0, kv[1]=1
kv = 3, kv[0]=1, kv[1]=1
=> [0, 1]Now let’s try another array;
irb(main):005:0> reverse_lookup(1, [Array, NilClass])
NoMethodError: undefined method `[]' for NilClass:Class
from ./reverse_lookup.rb:2:in `reverse_lookup'
from ./reverse_lookup.rb:2:in `reverse_lookup'
from (irb):4
from :0To sum this up, “duck types” aren’t just collections of messages, whether or not an object will work in any given role also depends on subtleties of the semantics of theimplementation of the corresponding methods, and can also depend on the state of the object as well.
Now this shouldn’t be considered a weakness of “duck typing” compared to “strong typing,” or something in-between the two. Strong-typing systems have similar “flaws” for example strongly typing a variable to an integer won’t protect against division by zerowhcn the variable is used as a divisor, nor will it alone protect against out of bounds errors when it’s used as an array index. The errors which aren’t detected by strong-typing is a much larger set than those which are. In languages which provide limited run-time checking of code which has passed the strong-typing “hurdle” the effects can be disastrous. In my humble opinion, the benefits of dynamic typing far outweigh “unlearning” what has been taught by other languages such as C++ and Java.
Bjarne Stroustrup used to quip, in panel discussions over strong vs. dynamic typing (i.e C++ vs. Smalltalk), that he’d hate to be a passenger on a plane which flashed a light labled “message not understood” in the cockpit when the pilot tried to lower the landing gear. I’d hate even more to be a passenger on a plane whose control software seg-faulted under this condition instead because putting the landing gear down caused a buffer overflow.
The real answer to uncovering and routing out errors is testing, testing, and more testing.









