Before we start, my answer would be 100 duck-sized horses.
Duck typing is one of the most important concepts that every Ruby developer should have a strong understanding of.
When I started learning Ruby, I remember struggling with the idea of duck typing. It seemed liked an easy idea to grasp, however, it’s one of those ‘easier said than done’ sort of things.
For example, here is nearly how every other post/video introduces the concept:
If it walks like a duck and quacks like a duck, then chances are….it’s a duck
Reading that you are probably thinking:
"That sounds pretty easy"
But when you get down to it, it’s much more complex than that. However, despite how overused the ‘quacks like a duck..’ saying is, it would still have to be the best way to describe it.
Assumption over Fact
If you have come from any other statically typed language such as Java, you may have noticed that in Ruby, we don’t define our object types.
E.g. we don’t:
Array animals = ['dog', 'cat', 'pig']
In reality, we really couldn’t give a rats arse about the type of an object in ruby; that is unless it acts like what we think it should .
What I mean by this is that I don’t care what type an object is, as along as it responds to the messages (methods) I send it, I’m happy.
This very feature is one of the reasons that makes Ruby so easy to write and read.
Let’s illustrate this concept with an example.
For this example let’s say you are trying to write a script which will mark an object as ‘dirty’ when one of its attributes has been changed:
def mark_dirty object object.dirty = true end
#mark_dirty method takes any object and sets the
dirty? value to
true . So how would this work?
Disclaimer: The below code is absolutely horribly written however it’s just used to illustrate the point.
class Dirty attr_accessor :dirty end class Dog < Dirty def initialize breed @breed = breed end end class Car < Dirty def initialize model @model = model end end class Relativity < Dirty end def mark_dirty object object.dirty = true end # Initialize the Objects dog = Dog.new 'Boxer' car = Car.new 'Audi' theory = Relativity.new # Roll 'em around in the mud mark_dirty car mark_dirty dog mark_dirty theory # Check if they're clean puts car.dirty # => true puts dog.dirty # => true puts theory.dirty # => true
So what’s happening here?
Well, firstly we create a
Dirty class. All this class does is give every instance of it an
attr_accessor :dirty method.
We then have three separate classes which all inherit from this
Dirty class, with two out of the three (which ain’t bad) having their own
.initialize method setting various attributes.
We then have the
.mark_dirty method from before and call this three times, passing an instance of
Relativity as an argument.
If you check the output, you will notice that all three instances have a
dirty value of
true thanks to the
attr_accessor :dirty in the
We didn’t care whether or not the object that was passed into
.mark_dirty had a method of
dirty on it, just that it responded to having
dirty set to true.
We have created a
DirtyObject ducktype i.e. We have objects that walk like they’re dirty and quack like they’re dirty even though they are three unique objects.
To use a real ruby example, objects such as
Hash could be argued that they have an enumerable ducktype as they respond to methods such as
In other statically typed languages, this would not have worked and you would have the
.mark_dirty method take only a certain type of object e.g.
def mark_dirty Array list list.dirty = true end
To summarize how duck typing works:
Focus on whether or not an object responds to a message opposed to what type of object it actually is.
So why is ducktyping useful? Well before in the above example, one clear benefit is that we only need one
In other statically typed languages, we would need a
.mark_dirty method for each of the three classes, making the code bloated and not-dry.
A problem with ducktypes though is that it can lead to exceptions being raised. This may lead to some using the
kind_of? method to check the class of an object that is passed into the method.
However, this really isn’t the best approach. Why?
Well because if you think of above example, we would have to check that the object passed in was either a
Relativity . Three isn’t so bad, but what happens if you have 100 objects that respond to that method? You might then say to just call
object.class.superclass to see whether or not it inherits from
Dirty . However what happens when you have a class that inherits from
Dog ? Calling
object.class.superclass will return
Dog and therefore
kind_of?(Dirty) will return false, even though the object does respond to
Another argument might be to call
respond_to? on the object which is right in checking whether or not the object responds to the method however it can fail if
method_missing has been altered.
Dave Thomas, Author of the famous PickAxe answered this one perfectly so I’ll leave it to him.
“You don’t need to check the type of the arguments. If they support << (in the case of result) or title and artist (in the case of song), everything will just work. If they don’t, your method will throw an exception anyway (just as it would have done if you’d checked the types). But without the check, your method is suddenly a lot more flexible: you could pass it an array, a string, a file, or any other object that appends using <<, and it would just work.”
I hope this post helped explained this concept to you – if you have any questions or suggestions please let me know in the comments below!