Just said:
By "it's a reduction ad extremum of a way of constraining object
behaviour" do you mean that it reduces object behaviour in an extremely
non-constraining way?
Yes. In the object-message interpretation of OO, it means an object only
needs to respond to the messages absolutely required in a context to be
qualified to be of the valid type.
With statically typed languages, the behaviour of an object is defined
by membership. If this variable is a member of this set, then it may have
these properties. So, when a function requires that a parameter be of a
certain type, it must be a member of this certain set. The use of other
types that may also work in this function but are not in this set are
(arbitrarily) excluded.
With dynamically typed langauges, because variables (including
parameters) may be of any type, parameters passed into a method will work
as long as they, literally, satisfy the requirements of the method. This
is much more flexable than defining arbitrary membership. It's "ad
extremum" in the direction of flexability. Unfortunately, it's also
flexable enough to allow you to pass a totally inappropriate parameter into
a method but that's the trade off. Is it worth it? Most of us think so...
<pedant>
*flexibility
</pedant>
As Chad Perrin pointed out, you leverage the benefits of dynamic typing
in the special / edge cases rather than the common case. If you poked
around Ruby libraries, you'd find out at least several, if not many
cases when the libraries perform strong type checks on objects passed
into them.
And for what it's worth, I personally don't see a problem with this. It
makes the libraries easier to document, easier to understand, and behave
more predictably. Ruby -allows- you to use (and abuse) dynamic typing,
noone's saying you need to code to allow for it. I would personally
advocate the opposite, write predictable code most of the time, and then
do parts of it that could benefit from the dynamism by design - since
they will be inherently more error-prone, they require a lot more effort
to be written in a robust maintainable way.
If you want an example you can use in the future, how about a function
or method that takes a vector and a list of vectors and sees if the given
vector can be expressed as a linear combination of the list of vectors.
Because so many things have vector properties (real numbers, complex
numbers, Cartesian vectors, matrices, real valued functions, almost
anything, it seems), you can reuse this function or method with any of
these types! How amazing is that?
Interesting example there. Of course, I'd personally formalise the
vector properties somehow, at least in documentation. You want these
vector properties to manifest themselves in the different types of
objects in a consistent way, and this is a constraint on the parameters
of the function already. And in good-quality code, you want to check on
whether the constraint is satisfied and fail early if it doesn't.
Even dynamic / duck typing isn't completely eschewing type checks, even
if you can get away with that for spectacularly long. And the problem is
that expressing the parameter constraint with "is a Vector" is a little
clearer than "has vector properties". It's not -that- apparent in this
example, but for more complex ones, where you need a larger set of
behaviours from the function parameters, the formal granular constraints
on them would be very verbose and hard to understand. Having "ubmrella"
concepts that are readily understood is very helpful in that case. And
the path between concept and code is sometimes a very short one - having
a mixin Vector module with documented methods that you expect from an
object "with vector properties" and some helper methods you'd use would
probably be my first move after making a vector manipulation library
with the dynamism you describe.
David Vallner