E
Eric Mahurin
I've seen many posts on what duck-typing is, that Ruby doesn't
need static typing, that duck-typing results in easy to read
code, etc (and others that want static typing). What I haven't
seen much of is what advantage duck-typing gives to a method
over conventional polymorphism. This is a follow on of my
previous thread "making a duck". I would like to get on my
soapbox and talk about it a little...
With the polymorphism of other languages, class hierarchy is
very important. When you specify an argument can take a
certain class, you are saying that it can also take anything
derived from it (or implementing the same interface if the
language has that concept).
With duck-typing, class hierarchy and class names are not
important. Every argument that is duck-typed (built-in methods
unfortunately usually aren't) effectively has its own "type".
The "type" of an argument is defined by what methods it needs
to respond to and any "type" requirements of those methods
arguments or return values. If an argument has no method
requirements, then that argument could be anything. With an
argument needs to respond to just one method, you can think
about making an object on-the-fly that meets that one
requirement. That one method could easily be made from a
method of another object - including Proc#call (arbitrary
code). With an argument that needs to respond to more methods,
you can still do the same (mapping multiple methods), but at a
certain point it may become too cumbersome.
As an example, suppose you have 2 methods in an object that
take String-like things. With conventional polymorphism, you
might abstract this up to anything indexable to encompass
String and Array. With duck typing, you don't need to do
anything explicit - just continue thinking of it like a String
when coding the methods. Let's say one of the methods reads
from its "String" argument using only the [] method. You could
easily use a String, Array, Hash, or more powerfully a Proc.
The second appends to its "String" argument using the <<
method. Here you could easily use a String or Array. In both
of these cases, you could grab any method of any object and map
it to new object (and name the method appropriately). Of
course the most flexible would be mapping a Proc#call - do
whatever you want in that Proc.
To get maximum benefit from duck-typing, several things will
help out:
* For every duck-typed argument, document what the duck-type is
- what methods it needs to respond to and what the duck-type
requirements are on those method arguments and return values.
To help out with this it would be nice if we had something in
rdoc for this - but I don't know what.
* A convenient way to create objects on-the-fly for duck-typed
arguments. Some implementations were given in the thread
"making a duck".
* Don't overload your methods. As soon as you overload a
method to allow multiple "types" for an argument (and do
different things based on the "type"), you are not doing strict
duck-typing. At a minimum when you overload, you have to ask
whether an object responds to a certain method to determine
what to do. Doing this can limit what you can pass in due to
ambiguities as to what the "type" is. If you make a method
that can take a flying-thing, a swimming-thing, or a
walking-thing, what should this method do when it receives a
duck - because a duck can fly, swim, and walk?
* The fewer methods that a duck-typed argument responds to the
more flexibility you have in what you can pass to to it. Many
arguments may just require one method of the argument which is
ideal (next to zero methods of course). One method allows easy
on-the-fly object creation as described above.
* Use duck-typing everywhere!
__________________________________
Discover Yahoo!
Have fun online with music videos, cool games, IM and more. Check it out!
http://discover.yahoo.com/online.html
need static typing, that duck-typing results in easy to read
code, etc (and others that want static typing). What I haven't
seen much of is what advantage duck-typing gives to a method
over conventional polymorphism. This is a follow on of my
previous thread "making a duck". I would like to get on my
soapbox and talk about it a little...
With the polymorphism of other languages, class hierarchy is
very important. When you specify an argument can take a
certain class, you are saying that it can also take anything
derived from it (or implementing the same interface if the
language has that concept).
With duck-typing, class hierarchy and class names are not
important. Every argument that is duck-typed (built-in methods
unfortunately usually aren't) effectively has its own "type".
The "type" of an argument is defined by what methods it needs
to respond to and any "type" requirements of those methods
arguments or return values. If an argument has no method
requirements, then that argument could be anything. With an
argument needs to respond to just one method, you can think
about making an object on-the-fly that meets that one
requirement. That one method could easily be made from a
method of another object - including Proc#call (arbitrary
code). With an argument that needs to respond to more methods,
you can still do the same (mapping multiple methods), but at a
certain point it may become too cumbersome.
As an example, suppose you have 2 methods in an object that
take String-like things. With conventional polymorphism, you
might abstract this up to anything indexable to encompass
String and Array. With duck typing, you don't need to do
anything explicit - just continue thinking of it like a String
when coding the methods. Let's say one of the methods reads
from its "String" argument using only the [] method. You could
easily use a String, Array, Hash, or more powerfully a Proc.
The second appends to its "String" argument using the <<
method. Here you could easily use a String or Array. In both
of these cases, you could grab any method of any object and map
it to new object (and name the method appropriately). Of
course the most flexible would be mapping a Proc#call - do
whatever you want in that Proc.
To get maximum benefit from duck-typing, several things will
help out:
* For every duck-typed argument, document what the duck-type is
- what methods it needs to respond to and what the duck-type
requirements are on those method arguments and return values.
To help out with this it would be nice if we had something in
rdoc for this - but I don't know what.
* A convenient way to create objects on-the-fly for duck-typed
arguments. Some implementations were given in the thread
"making a duck".
* Don't overload your methods. As soon as you overload a
method to allow multiple "types" for an argument (and do
different things based on the "type"), you are not doing strict
duck-typing. At a minimum when you overload, you have to ask
whether an object responds to a certain method to determine
what to do. Doing this can limit what you can pass in due to
ambiguities as to what the "type" is. If you make a method
that can take a flying-thing, a swimming-thing, or a
walking-thing, what should this method do when it receives a
duck - because a duck can fly, swim, and walk?
* The fewer methods that a duck-typed argument responds to the
more flexibility you have in what you can pass to to it. Many
arguments may just require one method of the argument which is
ideal (next to zero methods of course). One method allows easy
on-the-fly object creation as described above.
* Use duck-typing everywhere!
__________________________________
Discover Yahoo!
Have fun online with music videos, cool games, IM and more. Check it out!
http://discover.yahoo.com/online.html