Duck Typing
Tweet this postIn a well known Chuck Jones cartoon, a female cat, called Penelope, accidentally has a white streak painted along her back. This causes a passing french skunk, called Pepe Le Pew, to mistake her for a female skunk.
Hilarity (?) and harrassment follow.
I’ve always found this perplexing, even as a child. Aren’t skunks and cats quite different? How can Pepe confuse a female cat with a female skunk?
I now understand that Pepe, with his admirably undiscriminating nature 1, is adhering to the wonderful philosophy of duck typing
1. Though his inability to understand that “no means no” is certainly not admirable.
What is Duck Typing?
In modern society, most of us try to adopt a non-prejudicial attitude to avoid unfair discrimination. When being non-prejudicial we don’t care what “type” of person we’re dealing with; we only care whether we can have good interactions with that person.
In programming we call this attitude “Duck Typing”. The term “Duck Typing” comes from a simple example of abductive reasoning:
If it looks like a duck, swims like a duck, and quacks like a duck, then it probably is a duck.
Duck Typing, however, goes further than pure abductive reasoning. Instead of merely saying that we can deduce the type of a thing from it’s behaviour, the principle of Duck Typing says that we don’t need to care about the type of the thing we’re dealing with, so long as we can have the interactions we want.
Duck typing in programs
In programming we often classify our objects into types, but, if we apply the idea of duck typing, the functions and methods we write don’t need to care about object types. As long as the objects they handle behave in all the right ways, why worry? 2 If it behaves like a duck then you might as well treat it like a duck.
2. Hakuna Matata
Here’s a silly code example (using the Ruby programming language).
An example in Ruby
Here’s a class definition for a farmer who can feed ducks.
class Farmer
def initialise(food)
@food = food
end
def feed(duck)
duck.eat(@food)
end
end
Here’s what the duck class looks like.
class Duck
def eat(food)
puts "quack quack. I like #{food}"
end
end
We can create a farmer instance (called giles) as follows.
giles = Farmer.new("grain")
We can create a Duck instance (called donald) as follows.
donald = Duck.new
We can tell farmer giles to feed donald the duck.
giles.feed(donald)
Great. All is well.
But wait! What if the farmer also has chickens to feed. He currently only knows how to feed ducks.
How much work do we need to do to enable farmers to feed chickens?
The answer to that question depends on how we think about chickens. We’ll look at two different ways of defining chickens: one that incorporates duck typing and one that doesn’t.
Chicken version 1 (no duck typing)
class Chicken
# chickens peck at there food
def peck(food)
puts "cluck cluck. I like #{food}"
end
end
Notice that chickens (as defined above) have a peck
method, but ducks (as defined earlier) have an eat
method. If we define chickens this way, then we have to change the farmer class to look something like this:
class Farmer
def initialise(food)
@food = food
end
def feed_duck(duck)
duck.eat(@food)
end
# Separate method for feeding chickens
def feed_chicken(chicken)
chicken.peck(@food)
end
end
And only now, after this change to the farmer class, can a farmer feed both ducks and chickens.
giles = Farmer.new("grain")
donald = Duck.new
chicken_little = Chicken.new
# Farmer giles needs to feed ducks and
# chickens differently.
giles.feed_duck(donald)
giles.feed_chicken(chicken_little)
In this version, we defined a new chicken class and changed the farmer class to handle the feeding of chickens. Hmm… Can we do it with less effort?
Chicken version 2 (with duck typing)
In the previous version we defined chickens as having a peck
method instead of the eat
method that ducks have. This time we’ll take duck typing into consideration when we define the chicken class and we’ll ask ourselves, “what if we make chickens behave more like ducks?”
class Chicken
# chickens `eat` their food (like ducks)
def eat(food)
puts "cluck cluck. I like #{food}"
end
end
Now ducks and chickens feed in the same way. They both eat
their food. By defining chickens this way, we don’t need to change the farmer class at all.
giles = Farmer.new("grain")
donald = Duck.new
chicken_little = Chicken.new
# farmer giles can feed ducks and
# chickens in the same way
giles.feed(donald)
giles.feed(chicken_little)
In this last version, farmer giles doesn’t need to care what bird he’s feeding. We could keep going and define classes for pigs and cows and so on, and we’d never need to change the farmer class. If it eats like a duck, farmers can feed it: that’s duck typing.
Summary
Engineering our objects with Duck Typing in mind can save us time and effort, and minimize changes to existing code. Why not apply duck typing in your code?