Even if you’re not new to ruby, the concept of ducktyping might evokes something vague and unclear to you. I personally had to re-read the dedicated section of my ruby book a couple of times before I finally grasped the concept.
What is ducktyping?
Ducktyping is the art of judging an object on what it can do rather than on what it is believed to be. What it means is that an object doesn’t live and die with a label pinned on it’s forehead. In ruby, an object can do anything it is able to do, isn’t it great? Yeah, it is.
It makes a lot of sense when you think about it. Suppose you have a method called be_scary that accepts one parameter representing a lion that will roar at the call of the roar! method.
def be_scary(the_lion) the_lion.roar! end
Even if the creator of this method seemed to have forget it, there are a lot of other living creatures on the earth that can express their might like that. No need to remind the programmer about that however, because we can use ducktyping to pass any scary stuff to our method. The thing that I will pass to the be_scary method do not need to share anything in common with a lion excepts for the fact that it can roar. Imagine for a second that scientists just discovered that rabbits could roar in some very specific conditions. No worries! You don’t have to touch a single line of your ruby program structure. Just pass the rabbit instance to the be_scary method and the interpreter will try to find it’s roar! method. Ruby doesn’t play a bouncer role like in many other languages. It doesn’t stand at the method gates while refusing the entrance to objects who are not exactly what the method is expecting to see. Instead, the ruby interpreter slacks off and let everyone enters into the method so they can try their luck with the roar! method.
Isn’t it the same thing than dynamic typing?
No, dynamic typing just makes reference to the way the type(or class) of an object is determined, that is, when you’ll start putting stuff in it. Ducktyping picks up from there and extends the capabilities. Generally, when ducktyping occurs, the interpreter knows the type of an object but still won’t exert authority on it by prohibiting it to enter some methods. We could say that a ducktyped language is always a dynamically typed language but not the other way around. A dynamically typed but not ducktyped language could still refuse objects based on their type at the doors of a method.
If you really want to catch exceptions before the ruby interpreter…
If you don’t feel comfortable with the fact that there is no bouncer at the doors of your method, you’ll be happy to know the existence of the respond_to? method. It kinds of fill the role of the typeof keyword in some other languages. However, instead of verifying if the object is a specific thing, you verify that it can do a specific thing.
See how we could have implemented our be_scary method.
def be_scary(something) if(something.respond_to?(:roar!)) something.roar! else raise "That thing cannot roar..." end end
Great… but if the only thing you are going to do is raise an exception (something ruby would have done by itself), this kind of practice may be a little overkill. It’s up to you to decide if the extra effort is worth it or not.
Ducktyping is not polymorphism
The beauty of ducktyping is that it doesn’t rely on inheritance or polymorphism at all. Every single objects are accepted in every single methods, period. The only thing that matters is the capability of an object to do whatever the method is trying to do. If the object can’t, the interpreter will gladly raise an exception in time (unless you use some respond_to technique that is).
Give ducktyping a real chance
Ducktyping can shake the conceptions you have about types and classes in programming languages. At first, I was not sure if I liked the idea, but now I think that it is a very logical and clever way to manage types. I came to the realization that programming languages didn’t need that many boundaries after all. I even wonder why no one thought about ducktyping before. Where’s the catch?
4 thoughts on “What's the fuzz about ducktyping?”
The catch is performance. Strong typing (including languages that do type inference, like Haskell) allow for compile-time binding to methods. This can allow for the actual call to the method to be decomposed a single JMP instruction. I’m not familiar with Ruby’s implementation of duck-typing specifically, but at best it will require a seek through the object’s method table, and at worst, it will require an operation on the level of reflection — which is essentially what you’re doing with the respond_to? statement, if I’m not mistaken.
There’s nothing WRONG with duck-typing, per se, but it trades off cpu cycles for programmer cycles. They said the same things about C vs. assembly, though, of course… 🙂 Software engineering is all about understanding costs of certain actions, so as long as you recognize the cost of duck typing, then there’s no real problem with using it. (Although, Ruby doesn’t give you much of a choice…)
Beyond performance, my major issue with duck-typing and Ruby in particular is that it destroys the clarity that you get when using a strongly-typed language (say C#). In C#, if an object extends a given supertype or implements a given interface, it’s asserting that it supports the functionality of that subtype/interface. Said another way, an instance of the subtype IS an instance of the supertype. (This is referred to as the Liskov Substitution Principle.) Inheritance can be thought of as a contractual relationship between the inheriting type and any external code that consumes it — the type is offering a guarantee that it supports the functionality of the supertype.
With Ruby, you don’t get the same guarantees, since the contractual assertions are moved from the type level to the method level. All you’re REALLY doing is delaying the contractual checks from compile-time to run-time. This certainly makes it easier to write an application the first time, but it significantly raises the overall complexity of the project, making the application more difficult to maintain in the future. Not to mention making it run dog-slow… or is that duck-slow? 🙂
Just my opinion!
Duck typing is a whole lot like late binding, where the object.method call is evaluated at the moment it is called. What’s very ruby-ish to me is adding a method to an instance of a class, vs adding a method to a class.
There are quite a few things wrong with duck typing, and code readability and maintenance is one of them. Read this for more details:
Thanks for the comments guys.
I can imagine ducktyping causing readability problems. Someone who relies too much on ducktyping could easily lose both his mind and the control he has on his own application. In fact, the link that Duck Skeptic put opened my eyes on some issues. In small doses (or just for testing/debugging), I’m still convinced that ducktyping is awesome. But I agree we should be careful of not overusing it. I’m going to experiment more on that to make up my mind.
I also really enjoyed the comment by Nate. Performance might be the catch, you’re right. However, I still ask to be proven that it can make an application run “dog-slow”. =) Of course, I guess it all depends on the level of performance you need. To me, a few milliseconds slower is still acceptable.
Thanks again for your input!