On Hunting Ducks

Posted by Rick DeNatale Wed, 26 Jul 2006 19:39:00 GMT

Five doctors go duck hunting, they draw lots to determine the order in which they will shoot from the blind.

The Psychiatrist gets the first chance.

A flock of birds fly over. He looks down at his shotgun and says

They look like ducks! They sound like ducks! I wonder how they feel about being ducks?

Meanwhile the birds fly by.

Next up is the internist:

They look like ducks! They sound like ducks! But we can’t rule out the possibility that they might be geese.

Then the general practitioner:

They look like ducks! They sound like ducks! What do you guys think?.

The surgeon takes his place next. He confidently feels the trigger of his shotgun with his highly skilled right forefinger. The next flock flys over.

Bang! Bang!

And the surgeon turns to the pathologist and says:

Check them out and see if they are ducks.

I’ve known this joke for quite a few years, but I’ve never thought about it in relationship to software, until I started playing around with Ruby, and discovered an old friend under a new name, “Duck Typing.”

Last week, I ran across a thread on the ruby forum called “I’ll have the Duck!” which quickly got lots of responses, and seems to have ended up with jokes.

Most of this thread seems to miss the point of duck typing.

Once you’ve really grokked duck-typing you become like that surgeon. Your perception of the properties of objects that really matter changes. And you learn to use the pathologist (testing) as a powerful ally.

Many programers coming to ruby from languages like C++ and Java are like the psychiatrist, internist, and general praticioner in the duck hunting story.

They want certainty in their typing. They’ve developed the expectation that early type-checking by a compiler will keep them from making mistakes.

The problem with this expectation of the power of early type checking is two-fold.
  • It doesn’t really work
  • It is overly constraining

It doesn’t work because the bugs caused by type-checking errors is a small subset of buges in general. Besides, in a fully object—oriented language like Ruby or Smalltalk, they are rare (because the methods which actually operates on the data is actually automatically strongly type-bound to the data by virtue of it being a method. It’s overly constraining because it unnecessarily couples the implementations of the provider and users of the services provided by an object.

Rather than droning on about the failures and contraints of early-type checking, which I suspect I’ll address more fully here as time goes on. Allow me to introduce the notion of types in an object-oriented languagethat I’ve come to accept over many years.

Let me suggest that in a fully object-oriented language types are not properties of objects or classes, but properties of variables which code using an object uses to refer to those objects. In saying this I’m recapping the key idea behind an IBM Technical Report I wrote back in 1991 entitled “Types fromthe Client’s Viewpoint” Which is available here as a pdf file.

“But wait a minute!”, you say. “Aren’t types for matching objects with variables, so that programmers won’t make mistakes when they manipulate the object?” To which I reply, “That’s not object-oriented.”

Now of course many will argue with this, but I’d like to revisit the original conception of object-orientation, a term which was coined by Alan Kay, the inventor of Smalltalk, a language from which Ruby has borrowed a lot.

The key idea behind “object-orientation” a la Smalltalk and Ruby is the encapsulation of implementation behind an interface defined by a set of messages. Kay felt that the traditional way of structuring software as programs which processed data, and the attendant separation of program and data was somthing to be avoided. His view is that it isbetter to compose software out of components which all look like little computers, which contain and bind the programs and state together. This was a synthesis of ideas which he had seen over many years, although they had never been codified before he did it.

For an insight into Kay’s thinking, have a look at the article he wrote some years ago about “The Early History of Smalltalk”

Another way of looking at this idea, is that objects should be like little server machines, the exact set of services available and their implementation can vary from server to server, but clients can still use those services in isolation from those details.

In both Ruby and Smalltalk, classes and inheritance are used for implementation sharing, in order to achieve economies in implementing those little computers (objects). This is not an essential feature in Kay’s conception of OO. The original versions of Smalltalk had neither classes nor inheritance which were introduced in later versions.

In parallel with the evolution of Smalltalk, a family of languages based around the concept of Abstract Data Types became an important Computer Science research topic. Abstract Data Types arranged data types into a hierarchy so as to categorize their use as a way to structure more traditional procedural programs, and they called these abstract data types, classes, and the relationship between classes inheritance, so subclassing was simply a synonym for subtyping.

Since, by the time Smalltalk had been made widely known by the publication of a special issue of Byte magazine devoted to Smalltalk-80 in 1981, it had added classes and inheritance, most observers mistakenly thought of classes and inheritance as essential features of the language. One of these was Peter Wegner who wrote an influential paper Dimensions of Object based Language Design which was published in the proceedings of the 1987 OOPSLA conference. In this paper, Wegner defined “object-oriented” languages as those cobining “objects+classes+inheritance.” What he missed was that in Kay’s conception of object-orientation classes and inheritance were merely implementation artifacts, and that the encapsulation of implementation within an object was the big idea which separates object-oriented languages from procedural languages, and despite the similarity in terminology, abstract-data-type languages, which presented instances of those data types as things to be manipulated from the outside, were far from object-oriented, at least as Kay conceived it.

So to wrap this up for now, duck-typing is a natural consequence of the nature of how objects with strongly encapsulated implementations should interact. The notion of objects containing the code which manipulates them, and hiding that implementation behind a wall, through which messages are sent, is a paradigm shift with powerful consequences. Because many so-called “object oriented” languages missed this paradigm shift, many of their users are uaware the power of thinking about software design from this new perspective.

I hope, that in some small way, this blog will help expose the beauty of this paradigm shift.


Trackbacks

Use the following link to trackback from your own site:
http://talklikeaduck.denhaven2.com/trackbacks?article_id=2

Comments

  1. Ed Borasky 25 days later:
    As long as we're telling jokes ... Two stockbrokers meet in the cardiac care unit, having just suffered heart attacks. The doctor tells them in no uncertain terms they have to change their lifestyles -- relax, take up a hobby. The two stockbrokers look at each other ... think for a few seconds ... then in unison yell out "Duck Hunting!" So ... they get the shotguns, the dogs, the decoys, the boat, the orange clothes ... the whole bit. They go out to the lake. And wait ... and wait. Five hours later, one of them turns to the other and says, "I think we must be doing something wrong." The other one says, "Yeah ... maybe we should throw the dogs up higher."
  2. Gregory Seidman 26 days later:
    Nice post, but there's a false dichotomy hidden in there. Early type checking is not incompatible with duck typing. In fact, one of my favorite things about C++ is the duck typing in the template language. (It is worth noting that C++ is really three layered languages. The source is passed through the C preprocessor, then the compiler interprets the template language, then the compiled and linked code is run. It is appropriate to think of the compiler as an interpreter for the Turing-complete templating language.) I like Ruby a great deal, and I'm delighted by its flexibility and dynamism, but sometimes I miss having a compiler that automates catching at least some subset of bugs with static analysis.
  3. Gavin Kistner 26 days later:
    Great article. I'd like a little more insight as to the role of the pathologist. In the joke, the pathologist is Ruby's runtime: only after the shooting of the ducks (method invocation) do you find out if you've made a mistake (RuntimeError). However, you refer to the pathology as 'testing'. It sounds like you're saying that an important part of ducktyping is writing something like: def foo( bar ) raise "Uh Oh" unless bar.respond_to?( :whee ) bar.whee end Is that your belief? Aside - you have a minor markup error in the article: "...an IBM Technical Report I wrote back in 1991 entitled Which is available here as a pdf file." That's a funny name for a report. ;)
  4. Rick DeNatale 26 days later:
    Gregory Seidman said: Nice post, but there’s a false dichotomy hidden in there. Early type checking is not incompatible with duck typing. In fact, one of my favorite things about C++ is the duck typing in the template language. Sorry Gregory, but I can't agree. C++ templates are some other kind of bird than ducks. They are effectively just macros for generating new abstract data types.
  5. Gavin Kistner 26 days later:
    Bah, I meant:
    def foo( bar )
      raise "Uh Oh" unless bar.respond_to?( :whee )
      bar.whee
    end
    That's what I get for not previewing.
  6. Rick DeNatale about 1 month later:
    Gavin Kistner said: Great article. I’d like a little more insight as to the role of the pathologist. In the joke, the pathologist is Ruby’s runtime: only after the shooting of the ducks (method invocation) do you find out if you’ve made a mistake (RuntimeError). However, you refer to the pathology as ‘testing’. It sounds like you’re saying that an important part of ducktyping is writing something like: def foo( bar ) raise “Uh Oh” unless bar.respond_to?( :whee ) bar.whee end Is that your belief? No. I was talking about testing using a testing framework, like Test/Unit not run-time testing. Develop and keep using a good set of unit tests as you develop, and you'll get far more than you get from a 'strongly typed' language, or trying to force a language like Ruby to be 'strongly typed.'