Someone suggested that the discussion of what duck typing is is getting
away from how it was originally used.
Perhaps I’m confused, or I’m not explaining what I think in the correct
way, so I’ll try again.
Maybe a better way of phrasing the concepts of duck typing is contract
typing.
If you look at Java, interfaces more-or-less define contracts between
objects. If you have an interface Appendable, that’s a contract that
says an object will
a) Have a method append()
b) Implement that in a certain way
a) can be had in Ruby via respond_to?, the use of which isn’t required
for duck typing. b) can be had more explicitly by using modules.
However, modules, in my mind, are more for reusing code between class
hierarchies without requiring multiple inheritance, rather than for
defining what methods an object implements (although they could be used
for the latter).
If you really want to talk about duck typing, you’d be talking about an
example along the lines of:
def add_elements(foo)
foo << "a"
foo << "b"
end
This method accepts an array (foo) and adds two elements to it using the
append operator. However, looked at more abstractly, this method takes
any object for which << implies append in an array sense. We could
actually pass in a binary search tree where << adds an element to the
three, and it would work in a similar way. If we pass in a Fixnum where
<< is bitwise shift, then we’ve broken the contract.
In Java, you need interfaces for this. You could write:
void addElements(Array foo)
{
foo.append(“a”);
foo.append(“b”);
}
void addElements(BST foo)
{
foo.append(“a”);
foo.append(“b”);
}
But it’s easier to have both implement Appendable, and have addElements
work on anything that is Appendable. Java needs interfaces because of
static type checking. In ruby you can just assume that an object
implements things correctly and use it as such, which, I think, is the
idea of duck typing.
If you want to make it more general, you can add a respond_to? :<< and
respond_to? :append, since different objects might implement either or
both of those. However, the key idea is that in ruby, the contracts are
dynamic. Using Java interfaces, to some degree, forces you to follow
the contracts more explicitly, since you have to knowingly say you
implement a contract and continue to break that contract. However,
assuming that the contract is documented somewhere in the code, and
everybody follows that contract, then there is no need to explicitly
define modules or classes just to define the contract in code, rather
than leaving it in documentation. If you do define modules for each
semantic, then you end up with each of your classes including 10
different modules just so that code can check and see if a class follows
some contract (look through the Java standard library and you’ll see
many classes like this). You’re effectively turning ruby into a
statically typed language where all the type checking is done by you at
runtime.
So another way of saying “duck typing” is “programming with implicit
contracts” or “implicit contract typing” or something like that, rather
than Java, where implicit is replaced with explicit. This is really
something of a feature of any language that isn’t statically typed. The
difference in the case of ruby is that, although you can check the class
of an object with is_a? (or something like that), there’s no way to tell
from the class itself whether it will satisfy the contract. You have to
assume it does.
So having respond_to? :quack doesn’t make it a duck. It’s also how quack
is implemented, and there’s no way to really check that without
testing. Duck typing means making assumptions.
Anyhow, that’s my take on it all, at the end of the day. I hope I’m not
too off base. If I am, then Dave can yell at me.