type(Duck)'ing: On Duck vs. Static Typing

by jesse in ,


Last night before bed - I sat down and read two articles that seemed to have grown some legs - the first being "Duck Typing Done Right" and the followup, "Answers to Duck Typing Done Right" (see the reddit comment thread). Ignoring the XML/DTD/Semantic web "solution" (see: Eby: XML is not the answer) offered for "doing it right" I got spun up on what "duck typing" really is, and what it really means for people (me) as (a) programmer(s). Invariably I didn't sleep so well. What is duck typing? I hope most people reading this know what it is - but here is what I think it is, and why I think that a lot of people miss the point. Admittedly, this is slightly rant-ish.

To quote wikipedia on Duck Typing:

In computer science, duck typing is a principle of dynamic typing in which an object's interface and attributes determine valid semantics, rather than having the language enforce rules regarding inheritance or require type casting.This allows an object to be interchangeable with any other object so long as they both implement sufficiently compatible interfaces, regardless of whether the objects have a related inheritance hierarchy. Duck typing is a feature of programming languages such as Smalltalk, Python, Ruby, JavaScript, and ColdFusion.

Duck typing is the notion that if I create an object, and I give it the attributes of a Duck - let's say, a bill, a quack, a scent and feathers - then what I am giving you when I pass this object to you is a Duck (or at least supports the Duck interface(s)). This is also known as a "gentleman's agreement" - if I tell you I am giving you something that quacks, has feathers and a bill, then you can trust I'm giving you a damned Duck (or something equally compatible). And there's the rub. People lie. Adults lie. Java/C/static typing people would lead you to believe that this type of flexibility is "bad" and that the "social contract" of typing should be enforced by an independent third party w.r.t: the Compiler/Interpreter.

My point is if it walks like a duck, smells like a duck, looks like a duck - but it's really a dog with no Duck interface, someone needs to acknowledge reciept of a PinkSlip(object) because they broke the contract - they knew that a duck was expected and they didn't have the politeness to wrap up their dog in a duck! who cares if it's a Dog in Duck's clothing if it can Dog.quack() when it needs to?

(note, I like this definition - "Duck typing is the system wherein you wish to pass something that requires a Duck object, and you only have a Dog, you have the decency to wrap it in a Duck suit.")

This is the beauty of Duck/Dynamic typing - I don't care if an "int" is really an integer as long as it acts like one. If you can fulfill the contract (the interface) that the method/function/etc you are about to call requires, i.e: the Dog() with Duck() interface, then by god, everything should be fine!

I see this kinda like a conversation:

Me: I want a Duck man.

You: Well, I have something that's got wings, a bill and that quacks

Me: I don't know... Does it have feathers?

You: Why yes, yes it does (def feathers(self): pass)

Me: Is it really a duck?

You: It's a Dog(Duck)

Me: Close enough for government work.

Note that his just so happens to align with my very libertarian world-view: What you do in your own free time is cool, as long as when you come over to my place you're civil according to my rules in my house.

The other side of the coin is the criticism that due to the lack of the interpreter nanny-state to protect against evil lying people from slinging things around, you can not "find bugs" until runtime. (by bugs, they mean syntax, type and other runtime errors).

The irony of this is that the same individual that is stating how Java/C++ is better in this regard is saying it while they're running xUnit Tests prior to a full build. And that bring me to my next point: A sufficiently large enough code base will enforce the contractual obligations of the objects labeled Duck() through unit tests. Testing is always the contractual enforcer of Code. If you say X does Y and it doesn't really do Y - then all the type checking-compiler-time magic in the world wouldn't save you.

I come from a QA background. I love testing. With python - I don't view a lack of a compile as a problem - I view unit testing and a dose of pylint/pychecker as the compiler. Not to mention - I can prove hundred of more assumptions in a unit test framework than I can in a compiler: I can't walk AST in my sleep, it's much easier for me to make a good, solid suite of unit-level tests (or hell - do it by hand!) to prove that my function/object/etc works/impersonates/runs as desired.

Not even compiler-jockies would suggest shipping something without comprehensive tests! So far, the only person I know who would suggest it also talks to cheese sandwiches.

In the age of the Agile Methodology - i.e. "Writing a Test before Code" - why do you need need someone/something else to contractually obligate you to a contract of type()? Why can't you - as a consenting adult, working with other consenting adults be responsible enough to agree to the contract and at least test it out.

Duck/Latent Typing buys you a lot of things - eval(), more interesting (better) polymorphism, among others, and of course static typing has it's benefits! In fact, I like static interfaces and types - see my previous horn tooting about Abstract Base Classes. I like/want the ability to force the contract/type when it's really important.

What I take umbrage too - and of course, this is outside of the scope of the original article(s) I linked to is the idea that a third party (the interpreter, a new language, my mom/wife/government) needs to tell me how to infer types, manage inputs, or build interfaces. The best type of implementation is the light one. If people want more rules to enforce the contract - then give it to them (ergo: ABCs in Py3k). Otherwise, leave those of us willing to sit down and learn to write comprehensive tests to prove the contract is fulfilled alone. Duck typing is not a lesser creature to your fascist compiler-state. It's just different, and puts more responsibility on both the user and creator of an object and an application.

(maybe we should call it "contract typing", and if you really get hung up, get over it with isinstance().)

Edit to add for the Haskell people: Haskell is on the list of learning, but learning it is of a lesser impact on the paycheck than Java/Python hackery. But, people should also look into it (if you really like functional programming).

Hello, Reddit.

Also see my followup - Schrödinger’s Type (is a namespace a box?)

For more delicious discussion/links, check these out (not everyone agrees with me!):

Static Typing