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
end
The method is intended to return an array containing keys in a “Dictionary” which map to the given value:
irb(main):001:0> load 'reverse_lookup.rb'
=> true

irb(main):002:0> reverse_lookup(1, { 'a' => 1, 'b' => 2, 'c' => 1})
=> ["a", "c"]
Now, I said that this was a “cooked-up” example to illustrate a point about “ducks.” It would probably be more proper to implement a method like keys_for_value in Hash or a module which could be used to extend Hash or other “dictionary” classes.

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]
The reason that it works at all with an array of integers is a bit of an anomally due to the fact that Ruby integers define the [] method as a bit reference. When kv is an integer kv1 and kv0 are respectively the least significant, and second least significant bits in the binary representation of kv.

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 :0
So this doesn’t work at all.

To 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.

Posted in  | Tags , ,  | no comments | no trackbacks

Comments

Trackbacks

Use the following link to trackback from your own site:
http://talklikeaduck.denhaven2.com/articles/trackback/7

Comments are disabled