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

May 30th, 2007 § 15 comments

Last night before bed — I sat down and read two arti­cles that seemed to have grown some legs — the first being “Duck Typ­ing Done Right” and the fol­lowup, “Answers to Duck Typ­ing Done Right” (see the red­dit com­ment thread). Ignor­ing the XML/DTD/Semantic web “solu­tion” (see: Eby: XML is not the answer) offered for “doing it right” I got spun up on what “duck typ­ing” really is, and what it really means for peo­ple (me) as (a) programmer(s). Invari­ably I didn’t sleep so well.

What is duck typ­ing? I hope most peo­ple read­ing this know what it is — but here is what I think it is, and why I think that a lot of peo­ple miss the point. Admit­tedly, this is slightly rant-ish.

To quote wikipedia on Duck Typ­ing:

In com­puter sci­ence, duck typ­ing is a prin­ci­ple of dynamic typ­ing in which an object’s inter­face and attrib­utes deter­mine valid seman­tics, rather than hav­ing the lan­guage enforce rules regard­ing inher­i­tance or require type casting.This allows an object to be inter­change­able with any other object so long as they both imple­ment suf­fi­ciently com­pat­i­ble inter­faces, regard­less of whether the objects have a related inher­i­tance hier­ar­chy. Duck typ­ing is a fea­ture of pro­gram­ming lan­guages such as Smalltalk, Python, Ruby, JavaScript, and ColdFusion.

Duck typ­ing is the notion that if I cre­ate an object, and I give it the attrib­utes of a Duck — let’s say, a bill, a quack, a scent and feath­ers — then what I am giv­ing you when I pass this object to you is a Duck (or at least sup­ports the Duck interface(s)). This is also known as a “gentleman’s agree­ment” — if I tell you I am giv­ing you some­thing that quacks, has feath­ers and a bill, then you can trust I’m giv­ing you a damned Duck (or some­thing equally com­pat­i­ble).

And there’s the rub. Peo­ple lie. Adults lie. Java/C/static typ­ing peo­ple would lead you to believe that this type of flex­i­bil­ity is “bad” and that the “social con­tract” of typ­ing should be enforced by an inde­pen­dent 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 inter­face, some­one needs to acknowl­edge reciept of a PinkSlip(object) because they broke the con­tract — they knew that a duck was expected and they didn’t have the polite­ness to wrap up their dog in a duck! who cares if it’s a Dog in Duck’s cloth­ing if it can Dog.quack() when it needs to?

(note, I like this def­i­n­i­tion — “Duck typ­ing is the sys­tem wherein you wish to pass some­thing 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 typ­ing — I don’t care if an “int” is really an inte­ger as long as it acts like one. If you can ful­fill the con­tract (the inter­face) that the method/function/etc you are about to call requires, i.e: the Dog() with Duck() inter­face, then by god, every­thing should be fine!

I see this kinda like a conversation:

Me: I want a Duck man.

You: Well, I have some­thing 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 gov­ern­ment work.

Note that his just so hap­pens to align with my very lib­er­tar­ian 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 accord­ing to my rules in my house.

The other side of the coin is the crit­i­cism that due to the lack of the inter­preter nanny-state to pro­tect against evil lying peo­ple from sling­ing things around, you can not “find bugs” until run­time. (by bugs, they mean syn­tax, type and other run­time errors).

The irony of this is that the same indi­vid­ual that is stat­ing how Java/C++ is bet­ter in this regard is say­ing it while they’re run­ning xUnit Tests prior to a full build. And that bring me to my next point: A suf­fi­ciently large enough code base will enforce the con­trac­tual oblig­a­tions of the objects labeled Duck() through unit tests. Test­ing is always the con­trac­tual 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 back­ground. I love test­ing. With python — I don’t view a lack of a com­pile as a prob­lem — I view unit test­ing and a dose of pylint/pychecker as the com­piler. Not to men­tion — I can prove hun­dred of more assump­tions in a unit test frame­work than I can in a com­piler: I can’t walk AST in my sleep, it’s much eas­ier 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 sug­gest ship­ping some­thing with­out com­pre­hen­sive tests! So far, the only per­son I know who would sug­gest it also talks to cheese sandwiches.

In the age of the Agile Method­ol­ogy — i.e. “Writ­ing a Test before Code” — why do you need need someone/something else to con­trac­tu­ally oblig­ate you to a con­tract of type()? Why can’t you — as a con­sent­ing adult, work­ing with other con­sent­ing adults be respon­si­ble enough to agree to the con­tract and at least test it out.

Duck/Latent Typ­ing buys you a lot of things — eval(), more inter­est­ing (bet­ter) poly­mor­phism, among oth­ers, and of course sta­tic typ­ing has it’s ben­e­fits! In fact, I like sta­tic inter­faces and types — see my pre­vi­ous horn toot­ing about Abstract Base Classes. I like/want the abil­ity to force the contract/type when it’s really impor­tant.

What I take umbrage too — and of course, this is out­side of the scope of the orig­i­nal article(s) I linked to is the idea that a third party (the inter­preter, a new lan­guage, my mom/wife/government) needs to tell me how to infer types, man­age inputs, or build inter­faces.

The best type of imple­men­ta­tion is the light one. If peo­ple want more rules to enforce the con­tract — then give it to them (ergo: ABCs in Py3k). Oth­er­wise, leave those of us will­ing to sit down and learn to write com­pre­hen­sive tests to prove the con­tract is ful­filled alone. Duck typ­ing is not a lesser crea­ture to your fas­cist compiler-state. It’s just dif­fer­ent, and puts more respon­si­bil­ity on both the user and cre­ator of an object and an application.

(maybe we should call it “con­tract typ­ing”, and if you really get hung up, get over it with isinstance().)

Edit to add for the Haskell peo­ple: Haskell is on the list of learn­ing, but learn­ing it is of a lesser impact on the pay­check than Java/Python hack­ery. But, peo­ple should also look into it (if you really like func­tional pro­gram­ming).

Hello, Red­dit.

Also see my fol­lowup — Schrödinger’s Type (is a name­space a box?)

For more deli­cious discussion/links, check these out (not every­one agrees with me!):

Static Typing

  • Haskell

    I hereby declare

  • King

    I hereby declare that all blog posts talk­ing about sta­tic typ­ing should be ignored unless the poster at least men­tions that he’s tried of at least one of the fol­low­ing lan­guages: Haskell, ML, O’Caml, Clean, or Mercury.

  • http://www.jessenoller.com jesse

    @HaskellPeople: Haskell is on the list — I tried to get started but I got dis­tracted by other things involv­ing paychecks.

  • gwen­hwyfaer

    @King: isn’t that rather like say­ing “I hereby declare that all blog posts talk­ing about British uni­ver­si­ties should be ignored unless the poster at least men­tions that he’s attended at least one of the fol­low­ing uni­ver­si­ties: Oxford, Cam­bridge, Durham, Kent or York”…?

  • http://blogs.sun.com/bblfish/ Henry Story

    Thanks for men­tion­ing my arti­cle “Duck Typ­ing done right”.

    The orig­i­nal point of that arti­cle was, as you repeat above, that duck typ­ing is imple­mented in many lan­guages as just sim­ple string com­par­i­son. So your
    dog.quack() exam­ple, is said to imple­ment the Duck inter­face just because both con­tain the “quack” meth­ods. The prob­lem is that some­one else can just as well have cre­ated a “quack” method on the dog which does some­thing com­pletely dif­fer­ent (per­haps a QUick Answer to A Cat: Kill). The fact that two strings are the same does not indi­cate they mean the same thing. For this you would need name­spaced meth­ods at the least. Then you could dis­tin­guish between animal.sound.quack() and animal.action.quack() per­haps. This would give your pro­gram a lot more gen­er­al­ity already: it would mesh bet­ter with wider libraries, and depend less on sub­tle con­tex­tual knowl­edge of the code base. You can then see how going with full fledged URIs gives you the largest scal­a­bil­ity for exchang­ing and mesh­ing information.

  • http://www.jessenoller.com jesse

    @Henry: Obvi­ously if you had Dog(Duck) and both had Quack() there would be no string com­par­i­son — it’s resolved sim­ply by the fact that Dog inher­ited Duck’s meth­ods, and then over wrote the orig­i­nal Quack.

    If Dog’s Quack() method does not in fact, Quack() then it is not a valid quack. Dog() by it’s very nature has vio­lated the con­tract for any­one who needs a Duck() but gets a Dog()

    For exam­ple, in python — you have __getitem__(): If I cre­ated a class/object (class myob(object)) and over wrote __getitem__() to say, parse an XML stream and make the bios beep instead of sim­ply return­ing a value from the local name­space — I am in vio­la­tion of a very sim­ple and basic con­tract within the language.

    Name spaced meth­ods are exactly that within Python. pkg.module.file.method() is a clear enough name space. Proper python mod­ule struc­ture means that given your exam­ple, we should have:

    animals.dog.sounds.quack()

    animals.dog.actions.quack()

    And so on. Which most python pro­grams are smart enough to do — that’s the glo­ri­ous nature of name­spaces. If you have 14 Quack meth­ods, they should each be con­tained within the proper namespace.

    Man I have to get a threaded com­ment system

  • Jonathan Allen

    The prob­lem I see with duck typ­ing is that it seems to be really, really easy to break compatibility.

    Lets say I add a Swim method to my Duck class. Then I change the move method to call Swim instead of Fly under cer­tain circumstances.

    If I was using an IDuck inter­face, then I would get a com­piler error on the Dog(Duck) class and know I needed to fix it.

    If I am just using Duck, I wouldn’t know there was a prob­lem with Dog(Duck) until either the unit tests catch it or, more likely, it crashes up at runtime.

  • Jake

    I’ve been read­ing all of these Static/Duck argu­ments, and I’ve worked with both Rails and C++. I don’t mind work­ing with either sys­tems, but when I stop and think about duck typ­ing it just seems impre­cise. I don’t like rely­ing on the name I hap­pen to give a series of meth­ods to define what it is.

    Espe­cially when work­ing w/ lots of peo­ple it seems like a good idea to be able to define inter­faces strictly by set­ting up a Duck abstract class and hav­ing every­body that sup­ports the duck inter­face inher­it­ing from it rather than rely­ing on peo­ple call­ing things the same thing. It seems like we’re tak­ing a step back to have to man­u­ally write tests to check things that used to be done for us automatically.

  • Joe
  • http://blogs.sun.com/bblfish/ Henry Story

    If Dog’s Quack() method does not in fact, Quack() then it is not a valid quack. Dog() by it’s very nature has vio­lated the con­tract for any­one who needs a Duck() but gets a Dog()”

    Well not really. As I see in most of these lan­guages nobody uses full name­spaced method com­par­i­son to iden­tify the method. If they did then you would be right, for the rea­sons I pointed to in my orig­nal arti­cle: your name­spaced method is close to being a URI for it.

    But in fact these lan­guages don’t do this. they just look to see that there is a method that is named “Quack”. If there is it gets called as if it was clear that was it means was the sound. But why could a dog not have a “quack” method that meant to kill a cat? There would be no inter­face bro­ken here. Just a clash of words, and we have those a lot in the eng­lish lan­guage. Things such as “bank” (the place you deposit your money at) and “bank” of a river… We dis­am­biguate eng­lish because we take con­text into account, just as a pro­gram­mer makes sure that when he gives objects to a method that will duck type on “quack” he makes sure never to give the method objects where “quack” means some­thing else. Notice how this has trou­ble scal­ing though.

    Any­way, I would be glad to be shown to be wrong. I just looked up a book on Ruby, and that con­firmed my think­ing. Do you have a pointer to a piece of code that could resolve the issue?

  • Paul Bod­die

    The prob­lem is that some­one else can just as well have cre­ated a “quack” method on the dog which does some­thing com­pletely dif­fer­ent (per­haps a QUick Answer to A Cat: Kill).”

    Yes, peo­ple might define per­verse meth­ods which have the same names as nor­mal ones, and there is a pos­si­bil­ity of method name “col­li­sions”, but this kind of prob­lem is addressed in many other fields within com­put­ing (as well as being a gen­eral dis­am­bigua­tion prob­lem), and the solu­tion doesn’t have to involve rigid inter­face hierarchies.

  • nes

    :> Ide­ally, IMO, two mes­sages with the same name should have
    :> the same mean­ing but pos­si­bly dif­fer­ent imple­men­ta­tions.
    :> Of course, “mean­ing” is some­what rel­a­tive, but the notion
    :> that two mes­sages with the same name should have the same
    :> “mean­ing” is very useful.

    :Like clothes.launder() vs money.launder(), or shape.draw() vs blood.draw(),
    :or matrix.norm() vs hi.norm() ? I’m afraid Eng­lish thrives on puns,
    :and the same word rou­tinely means rad­i­cally dif­fer­ent things across
    :appli­ca­tion areas. There­fore, to insist that a word have “one true mean­ing“
    :in a pro­gram­ming lan­guage is insist­ing that the lan­guage cater to one true
    :appli­ca­tion domain.

    http://groups.google.com/group/comp.lang.python/browse_thread/thread/c12d4b19d0f42f05/3acb79797dd3d3fd

    Most peo­ple are pretty good at guess­ing the seman­tic of words depend­ing on the con­text used. Hav­ing to define the pre­cise seman­tics of every word each time it gets used sounds pretty ver­bose to me. Of course this might poten­tially cre­ate some bug (and is source mate­r­ial for jokes by come­di­ans). BTW, Java inter­faces don’t even allow to imple­ment meth­ods of dif­fer­ent domains with the same name for a class, so Java inter­faces don’t even solve the prob­lem. I won­der how Haskell han­dles that.

  • Pingback: jessenoller.com - Schrödinger’s Type (is a namespace a box?)

  • Pingback: jessenoller.com - Duck typing + Wikipedia.

  • Pingback: Not quite getting it. - Page 3 | keyongtech

What's this?

You are currently reading type(Duck)‘ing: On Duck vs. Static Typing at jessenoller.com.

meta