The case against duck typing 2009/09/12
It’s a common enough argument to bear documenting once and for all. Rubytards and cheese shoppers (that is, Python programmers) always gang up on me because they love ducks. I’d rather the ducks stay out of my type checking, that’s all.
Take this (contrived, as Mike Malone loves to point out) example of
what can go wrong with duck typing. Your revenue-generating web
app deals in kittens and dollars so you have classes called
Kitten
and CreditCard
, both of which have
a method used to stringify them:
class Kitten(object): # ... def __unicode__(self): return """<img src="%s" />""" % self.src
class CreditCard(object): # ... def __unicode__(self): return "XXXX-XXXX-XXXX-%s" % self.cc[-4:]
If some HTML template requires merely something stringifiable, a
CreditCard
will do just as well as a Kitten
.
To prevent mishaps, the caller must first check the type of the potential
kitten or the Kitten
and CreditCard
classes must
be made different species of duck. While the latter is certainly
cleaner from the caller’s perspective, it (pathologically) results
in every method of the Kitten
class being prefixed
"kitten_
" to avoid calling a goose a duck.
Of course, this example was invented for the express purpose of making duck typing look awkward and dangerous. Long before it gets to that point, I think it crosses into confusion. Looking at a method signature written in any popular dynamic language gives the caller no instruction beyond (maybe) the number of parameters expected. If you’re lucky, the names of the parameters shed some light. Perhaps someone’s gone all Javadoc and written out comments that explain what the hell’s going on.
I am not an advocate of regressing back to statically typed languages for all programming (though it’s well-known I’m a big C++ fan). I want declared typing. Methods should state what they accept in such a way that the compiler can enforce this on our behalf. PHP 5 introduced type hinting [1] as a half-assed way to accomplish this. For objects and arrays (but not integers, floats and strings), PHP can automatically raise “catchable” [2] fatal errors when types don't match up as you intended.
While PHP’s solution is half useless for not working on
primitives and half again because you can’t deal with the error
and move on, the heart is in the right place. Declared typing is
for the programmer using the API your code creates. A compiler
saying unambiguously that you passed a Sailboat
when you
should have passed a Bulldozer
is delightfully useful when
compared to curious “method not found” errors. Worse
still is the subtly incorrect behavior introduced when somebody
quacks.
In a nutshell, I wish for the clarity I get from reading statically typed code with the power of dynamically typed languages.