Is Duck Typing a Type System, or a Way of Thinking?

Posted on January 4 2014 10:43 AM by jatten in CodeProject, C#, Javascript   ||   Comments (4)

6114395629_e28c3d1c65Ok, so there is what might be my first-ever "link bait" title. Especially dangerous because I am about to jump into a discussion propagated by some well-known, highly-respected developers against whom my own self-taught, inexperienced knowledge pales. 

I lack the formal schooling or training to join this discussion at the academic level, I simply know what I think I know, and will attempt to toss my opinion out there and see what happens. Actually, that is another first for me . . . I normally recognize that I don't have sufficient "real-world" experience or education to express a strong opinion on such things. Must be early Saturday morning, and I might need more coffee.

Image by Federico Reiven | Some Rights Reserved

Eric Lippert recently posted a typically well-stated article on his blog, Fabulous Adventures in Coding (if you don't already subscribe, now would be a perfect time to do so. His stuff really is THAT GOOD), setting out his thoughts relating Duck Typing to what we in the statically-typed world might recognize as Late Binding. When I woke this morning, I found in my inbox from Phil Haack's blog feed a follow-up in which aptly uses the square filters/round holes issue from the ill-fated Apollo 13 mission as an analogy for Duck Typing.

Both Lippert and Phil Haack appear to be attempting to reconcile the concept of Duck Typing with static type systems, and statically typed programming practices.

Mr. Haack appropriately pulls out some python code, but in my humble opinion only brings the Duck Typing concept partway home, by looking at method overriding in dynamically-typed languages as an example of how Duck Typing might be effectively demonstrated. Most importantly, Phil Haack hits on what, to me, is the deciding gem of the quest to understand:

"I think some of the confusion lies in the fact that duck typing isn't so much a type system as it is a fuzzy approach to treating objects as if they are certain types (or close enough) based on their behavior rather than their declared type. This is a subtle distinction to late binding."

-Phil Haack

Unfortunately (for me, anyway) he then goes on to discuss Duck Typing from a perspective of method overloading. Obviously, Phil Haack knows of which he speaks, and I in no way disagree with what he is saying. However, for myself, the important way to look at Duck Typing is unnecessarily obscured from this perspective.

My question is, are we trying too hard to pigeonhole a simpler concept?

Developers Moving from Statically-Typed to Dynamically-Typed Languages Tend to Over-Complicate Things

As one who has spent most of my time coding in a statically-typed language, I have developed some very basic coding practices and ways of thinking which make the transition to a dynamic approach most challenging. The mushy, malleability of dynamic languages and typing creates a situation which forces us to re-examine what we think we know about type systems, method signatures, and such (at least, if we come from a statically-typed background).

I have spent a lot of time trying to reconcile the dynamic mushiness with my static-typed tendency to create code which, through the action of compile-time type checking, forces the client of my code to "do the right thing."

If we examine some simple, but common design strategies in C# code, we can understand this tendency.

Interfaces Mimic the Notion of Duck-Typing in Statically-Typed Languages

When working in C#, Java, or other statically-typed language paradigm, we become very accustomed to working within the type system, and not only allow it to dictate much of our program structure, but also to assist us in protecting clients of our program from doing the wrong thing.

void TheAnimalSpeaks(Animal animal)
{
    Console.WriteLine(animal.WhatISay);
}

 

Before the code above will even compile, there are three things, at a minimum, which must be satisfied:

  • There must be an Animal class defined, and available to our code
  • The argument passed in to the method TheAnimalSpeaks must be of class Animal
  • The Animal class must itself define a WhatISay Property which resolves to a string

Therefore, we would need, at a minimum, a class Animal, which includes a property WhatISay:

The Animal Class:
class Animal
{
    public string WhatISay { get { return "I Said Something"; } }
}

 

In the C# world, we now know that any client code which wished to call the method TheAnimalSpeaks will be providing an instance of Animal as an argument, and that instance will indeed make available a property named WhatISay. We know this because the client code will not even compile until it does these things.

Also in C#, if we wanted to define different sorts of animals which all share some common behaviors and properties, we could define an IAnimal Interface, which would need to include the property WhatISay, and then modify our method TheAnimalSpeaks to accept an instance of IAnimal as an argument:

The IAnimal Interface in C#:
interface IAnimal
{
    string WhatISay { get; }
}

 

We would then Implement the IAnimal interface on any class we wished to exhibit the property WhatISay:

Classes Implementing the lAnimal Interface in C#:
class Duck : IAnimal
{
    public string WhatISay { get { return "Quack"; } }
}
  
  
class Wolf : IAnimal
{
    public string WhatISay { get { return "Howl"; } }
}
  
  
class Toaster : IAnimal
{
    public string WhatISay { get { return "Hi, I'm not even an animal!"; } }
}

 

Now, we can pass any instance of IAnimal to our method TheAnimalSpeaks and it will work. The compiler will make certain for us that, before we can even run our program, any code which calls this method is passing an argument which implements IAnimal.

static void Main(string[] args)
{
    // We can explicitly declare an instance of IAnimal . . .
    IAnimal theBigBadWolf = new Wolf();
    TheAnimalSpeaks(theBigBadWolf);
  
    //  . . . OR we can explicitly declare an instance 
    // of any type which IMPLEMENTS IAnimal:
    var theFleeingDuck = new Duck();
    TheAnimalSpeaks(theFleeingDuck);
}
  
static void TheAnimalSpeaks(IAnimal animal)
{
    Console.WriteLine(animal.WhatISay);
}

 

In the code above, we can either explicitly declare an instance of type IAnimal, or we can simply instantiate a type which implements the IAnimal interface, and our code will compile and run. We can also see that, so long as a class implements the IAnimal interface, it matters not what the actual class represents (as we can see from the Toaster class, which presumable has little or nothing to do with animals, but can still be passed to the method TheAnimalSpeaks, even though it is not necessarily a good example of domain modelling!).

This is Interface Inheritance 101, Dude. WTF, and Why Do I Care?

As a developer with a lot of background in a statically-typed environment, I found the transition to the dynamic world of JavaScript most challenging. In statically-typed land, we learn to expect that our method signatures, and compiler type checking will protect consumers of our code from passing in the wrong arguments.

In other words, the compiler enforces the contracts established by the method signatures we devise, and we carefully create our domain models to work within these boundaries. We devise  base classes and interfaces to define type expectations at program boundaries, and sleep well at night knowing that client code can't even call into our methods until certain minimum conditions are met.

When we move into a dynamically-typed environment, all that goes out the window.

Dynamically-typed languages do not perform any compile-time type-checking, instead relying on a run-time type system as described by Eric Lippert so effectively. All of the Type definition and interface business we discussed previously has little meaning in such an environment, because none of the advantages afforded by interfaces and the contracts they provide for the compiler are realized.

Duck Typing in Dynamic Languages

In my under-educated understanding, it seems to me common dynamic languages utilize Duck Typing implicitly. If seems to me we go to great lengths to describe a concept which should be fairly simple at its core:

From the Wikipedia definition referred to by Lippert:

"duck typing is a style of typing in which an object's methods and properties determine the valid semantics, rather than its inheritance from a particular class or implementation of a specific interface."

More Explicitly:

"In duck typing, one is concerned with just those aspects of an object that are used, rather than with the type of the object itself. For example, in a non-duck-typed language, one can create a function that takes an object of type Duck and calls that object's walk and quack methods.

In a duck-typed language, the equivalent function would take an object of any type and call that object's walk and quack methods. If the object does not have the methods that are called then the function signals a run-time error. If the object does have the methods, then they are executed no matter the type of the object, evoking the quotation and hence the name of this form of typing."

I have little background in compiler design, and I accept as axiomatic that under the covers, behind the scenes, there is a whole lot going on under the hood in the implementation of both the C# compiler, and the JavaScript interpreter to make both type systems work properly.

Duck Typing in JavaScript

However, to understand Duck Typing at the useful level of abstraction required to write code, the above definition can be illustrated by contrasting the previous C# examples with some simple JavaScript:

Let's consider some similar code in JavaScript. Note that JS does not have any notion of "classes." I will, however, model up some similar constructs using the JavaScript Module pattern (I am using the Node.js runtime here, such that I can execute the code in the terminal).

First, let's look at a Duck module:

A Duck "Class" (Module) in JavaScript:
var Duck = function() {
  this.whatISay = "Quack";
};
  
module.exports = Duck;

 

Here, we have defined a module which returns a "duck" object. We can do the same for our Wolf "class":

A Wolf "Class" (Module) in Javascript:
var Wolf = function() {
  this.whatISay = "Howl";
};
  
module.exports = Wolf;

 

Now, let's write a method analogous to our C# method TheAnimalSpeaks, and execute it in the terminal using Node.js:

Running the Javascript Equivelent Code:
var Duck = require("./Duck");
var Wolf = require("./Wolf");
  
this.theAnimalSpeaks = function(animal) {
  console.log(animal.whatISay);
};
  
var myDuck = new Duck();
this.theAnimalSpeaks(myDuck);
  
var myWolf = new Wolf();
this.theAnimalSpeaks(myWolf);

 

When we run the code above, the output in the terminal is, predictably:

Terminal Output from theAnimalSpeaks method in JavaScript:
Quack
Howl

 

As we can see, the JavaScript method theAnimalSpeaks cares not one whit what kind of object we pass in to it. In fact, it doesn't really recognize the argument's type until runtime, and even then, only recognizes that it is to look for a property on the passed in argument named whatISay. If it finds such a property, it will attempt to use it. If not, it will return the value undefined. If, instead of a property we were attempting to access a method, the JS runtime would throw a runtime error indicating that no such function exists.

Not Duck Typing in Javascript

Let's make a JavaScript analog of our Toaster class, but let's give it some sort of "toaster-like" method, and not include the whatISay property:

The JavaScript Toaster "Class" (Module):
var Toaster = function() {
  this.startToast = function(minutes) {
    console.log("the toast will cook for " + minutes + " minutes");
  };
};
  
module.exports = Toaster;

 

Here, we have created a "class" with a single method - the startToast() function which accepts an argument specifying how long to cook the toast. If we modify our application code to call into the Toaster module, attempting to both access the whatISay property as well as call the startToast() method, we see in the terminal output that the attempt to access the non-existent property returns undefined, and the call to startToast() executes as expected.

ApplicationCode Consuming the Toaster "Class" (Module):
var Duck = require("./Duck");
var Wolf = require("./Wolf");
var Toaster = require("./Toaster");
  
this.theAnimalSpeaks = function(animal) {
  console.log(animal.whatISay);
  animal.startToast(4);
};
  
var myToaster = new Toaster();
this.theAnimalSpeaks(myToaster);  

 

The terminal output from the above is:

Terminal Output from Application Code Consuming the Toaster "Class" (Module):
undefined
the toast will cook for 4 minutes

 

Obviously, the above is NOT an example of effective Duck Typing, as we are mixing metaphors and domain objects here. In similar fashion, let's try to pass one of our animal objects into the code as it currently exists:

Passing a Duck object into the Modified Application Code:
var Duck = require("./Duck");
var Wolf = require("./Wolf");
var Toaster = require("./Toaster");
this.theAnimalSpeaks = function(animal) {
  console.log(animal.whatISay);
  animal.startToast(4);
};
  
var myDuck = new Duck();
this.theAnimalSpeaks(myDuck);

The code above throws an error informing us that the object has no method startToast().

If it Acts Like a Duck, and Quacks Like a Duck, we can Use it like a Duck

The contrived examples above partially illustrate the power and danger in the Duck Typing concept. We have taken a method which accepts an argument, animal, and attempts to access certain properties and/or methods that it expects to find. There is nothing in the method "signature" beyond the name of the argument to tell us what methods and properties are expected, and the compiler is not going to help us prior to runtime. But if that object presents the expected properties/methods called by the code, all is well.

This is in keeping with the Wikipedia definition:

"duck typing is a style of typing in which an object's methods and properties determine the valid semantics . . ."

Let's look at one more silly example of how Duck Typing might be of use (or danger, as the case may be).

Modifying an Object at Runtime to take a Duck Typed Approach

Returning to our application code, lets ditch the call to the startToast() method, which we used previously only to illustrate how calling a non-existent function throws an error. Let's instead return to the idea that the method theAnimalSpeaks() expects to receive an argument animal, and that it expects the animal to have a property named whatISay which returns a string:

The Original Application Code:
var Duck = require("./Duck");
var Wolf = require("./Wolf");
var Toaster = require("./Toaster");
  
this.theAnimalSpeaks = function(animal) {
  console.log(animal.whatISay);
};
  
var myDuck = new Duck();
this.theAnimalSpeaks(myDuck);
  
var myWolf = new Wolf();
this.theAnimalSpeaks(myWolf);

 

Now, let's take things one more step in the direction of the ridiculous. Let's modify our myToaster object instance at runtime, adding a property whatISay, and then passing it to the method TheAnimalSpeaks:

Modified Application Code using a Duck Typed Approach:
var Duck = require("./Duck");
var Wolf = require("./Wolf");
var Toaster = require("./Toaster");
  
this.theAnimalSpeaks = function(animal) {
  console.log(animal.whatISay);
};
  
var myDuck = new Duck();
this.theAnimalSpeaks(myDuck);
  
var myWolf = new Wolf();
this.theAnimalSpeaks(myWolf);
  
var myToaster = new Toaster();
myToaster.whatISay = "I'm not an animal, but I speak and make toast";
this.theAnimalSpeaks(myToaster);   

 

Terminal output from the example above is:

Terminal Output from Modified Application Code:
Quack
Howl
I'm not an animal, but I speak and ost

 

Here we have, for better or worse, seen the idea of Duck Typing in action. We passed three different objects to our method theAnimalSpeaks. In all three cases, the object looked and acted like what the code was expecting, and so the runtime was able to properly execute the method.

In the case of the toaster, we forced the object to "look and act" like an animal for the purposes of our method call.

As our Wikipedia definition says:

" [. . . a Duck Typed] function would take an object of any type and call that object's walk and quack methods . . . If the object does have the methods, then they are executed no matter the type of the object . . ."

Is Duck Typing a Type System, or a Way of Thinking?

To me, as I try to write better, more idiomatic JavaScript code (I have a long, long way to go here), I find for the moment that learning to "think duck typing" is more valuable than attempting to define it in terms I relate to static type systems.

I am not sure one can say that a Duck Typing represents a Typing System per se. One might instead subscribe to the notion that to varying degrees, certain dynamic languages afford us the ability to employ a Duck Typed approach.

One line in the Wikipedia Entry that is not given the attention I feel it deserves by mssr.'s Lippert or Haack is:

"Duck typing is aided by habitually not testing for the type of arguments in method and function bodies, relying on documentation, clear code and testing to ensure correct use."

This has been my experience when working with JavaScript so far. You want to call a method, you need to figure out what it expects as arguments, and make sure whatever you are passing in has the properties and/or methods expected by the method implementation. And THAT'S IT.

To my mind, JavaScript (as an example) allows us to design a method which accepts an argument of whateverTheHellIWant, and the code will run properly so long as the argument implements the properties and behaviors expected by the method code.

Obviously to me, we want to adhere to some pretty solid design and naming conventions here. Additionally, there is probably no more clear case for effectively documenting an API such that consumers can more easily understand what is expected when calling your code.

In Dynamic Languages, the Interface is Implicit

Another way to approach the idea is to return to our concept of the interface in statically-typed systems. In these cases, and in our C# examples at the beginning of this article, we can see that interfaces in a statically-typed scenario allow us to say "This object looks and acts like a duck" even if it is not.

In a dynamic language, the ceremony associated with compile-time type checking, interface declaration, and such is unneeded. There is no compiler to enforce the type system rules, or to make sure our object implements a particular interface. This affords us an exceptional degree of freedom in designing our application code, and also places on us, the developer, the responsibility for ensuring our code will function correctly.

Final Thoughts

The preceding was inspired by both my own quest to better understand the languages I am using, and by reading two articles by developers I admire, and who know better than me. If anyone thinks I am trying to argue the points they make, please understand I am not. I am merely trying to synthesize what those two luminaries wrote with my own understanding, at the level that works best for me. If it helps anyone else, so much the better.

I am new to the world of Dynamic Languages in general, and JavaScript specifically. I am not a compiler builder, nor do I have any sort of academic training in language design.

Additional Resources and Items of Interest

 

Posted on January 4 2014 10:43 AM by jatten     

Comments (4)

Comments (4) -

Nissen
Nissen
1/6/2014 5:28:26 PM #

Duck typing is really the old recommendation of programming against interfaces instead of classes. The difference is that in static typed languages you define interfaces and in dynamic typed languages you have to remind people to do nothing (i.e. please don't check against specific types).

One difference is that you typically don't define one new interface per function so your interface normally has more methods than the ones you are actually using in that function. In dynamically typed languages you can think of each function accepting an object with an interface containing exactly the methods used by that function. The benefit is that you don't have to add dummy methods to your object if you want to use just that function just to make the interface happy.

XIV-Admin
XIV-Admin
1/6/2014 9:23:07 PM #

Nicely put!

I agree, and I think that's what I was trying to say as well. For a relatively simple concept, I found this one difficult to express. Or maybe I was still finding my way to what I wanted to say.

The gist of it for me was that perhaps, when folks from a statically-typed background begin discussing such a topic, they might tend to over-think it a little.

Thanks for dropping by, and for taking the time to comment! Cheers! Smile

Adam
Adam
1/28/2014 6:39:17 AM #

Don't forget the inclusion of the DLR! C# now can, essentially, perform duck typing of its own due to the fact that you can now make dynamic objects (System.Dynamic.ExpandoObject, while hilariously named, is insanely powerful and I LOVE it).

This leads to bundling a C# application with, say, IronPython (another wonderful addition to .NET, IMHO). That alone makes .NET a bit more powerful than most runtimes (the ability to transition to and from statically typed to loosely / duck typed systems with relative ease), even in the debugger itself (Start in C#, pass through to Python, return to C#).

It's incredible!

XIV-Admin
XIV-Admin
1/28/2014 7:48:15 PM #

Adam -

Totally agree. I've been messing about with Dynamics, and it is truly fun. Dynamics + reflection = great possibilities. Smile

Thanks for taking the time to comment!

-J

Comments are closed

About the author

My name is John Atten, and my "handle" on many of my online accounts is xivSolutions. I am Fascinated by all things technology and software development. I work mostly with C#, JavaScript/Node, and databases of many flavors. Actively learning always. I dig web development. I am always looking for new information, and value your feedback (especially where I got something wrong!). You can email me at:

jatten at typecastexception dot com

Web Hosting by