2 November 2006
Lame Duck Typing

“If it walks like a duck and quacks like a duck, it must be a duck.”

While true, sometimes this must be combined with another epigram to arrive at what Ruby gives you:

“If it walks into a forest and quacks but there’s nobody there to hear it, is it a duck?”

The problem isn’t with duck typing, per se, but in testing the duckiness of an object. Consider a foo object that has a bar method, and a mumble object that also has a bar method. You can define a method that calls bar on an object that’s passed in:

def call_bar(object)
  object.bar
end

and foo or mumble can be passed in with no ensuing calamity. And happily,

foo.respond_to? :bar

and

mumble.respond_to? :bar

return true as expected.

The real fun in Ruby starts when the bar method doesn’t exist in an object, but is created by the method_missing method dynamically. In this scenario, the introspection part of duck-typing can break down.

For instance:

class Fooble
  def method_missing(m)
    Fooble.class_eval "def #{m.id2name}() #{helper_for m} end"
    self.instance_eval "#{m.id2name}"
  end
end

fooble = Fooble.new

fooble.respond_to? :bar

returns false,

call_bar(fooble)

calls fooble’s bar, which faults to method_missing, which creates the bar method in Fooble and returns whatever the code provided by helper_for(:bar) returns, and then

fooble.respond_to? :bar

returns true!

This is Lame-Duck Typing. Duck Typing that may not be effective all of the time.

Granted, this is a bit contrived, but in such scenarios testing has to be equally contrived. What it amounts to is a logical race condition. In systems that can create code at run time, introspection can be questionable.

Be careful!