This punchline comes first, so if you've already made up your mind about type systems and dynamic languages and Perl 6, you can read something else: optional typing solves a lot of tricky problems in Perl 5.
When the desires for modularity, genericity, and correctness conflict, Perl 5 programmers have to fall back on heuristics. You can write quick and dirty and loose code that accepts whatever it gets and tries to do something sane with it, but you give up some ability to produce good error messages when something goes wrong and you run the risk of misinterpreting the intent of other programmers. The amount of risk varies on what you do and why, but it's there.
You can write strict code that checks everything against a severe whitelist, but you run the risk of forbidding people from doing necessary things and forcing them to contort their solutions in unnatural ways to conform to what you predicted they would need. The amount of contortion varies on what you do and why, but it's there.
(This dialectic informs the design of programming languages as well.)
My recent posts have described the conflict between encapsulation and manual type checking in Perl 5, how the available Perl 5 primitives provide wrong and misleading and incomplete information about types and capabilities, and why robust Perl 5 programming should worry more about what an object does than how it does it.
If you accept the idea that your Perl programs can be more robust and that error checking and loosely-coupled genericity are both desirable, the next obvious question is how to achieve both. Sadly, you can't — not as Perl 5 exists today.
The question Aristotle and I have debated in comments is how to judge the intent of two programmers: the one writing the API and placing constraints on parameters and the one using the API and providing arguments. The problem with heuristics is subtle but pervasive. The only evidence available to judge that intent is indirect. Certainly on the callee side I can detect whether a provided parameter is an object (a class less so), or whether it has overloading, or whether it uses a hash reference for its representation.
I can't tell why.
On the caller side, it's equally as important to know what the callee expects from its arguments. Here it's safer to assume that a function which takes a hash reference will treat that argument as a hash, but it's not always that easy. Will a function modify a string in place? (Can your language modify strings in place?)
Documentation can help... but if I believed documentation quantity made up for poor design, I'd use PHP.
Here's the punchline again. If Perl 5 supported a mechanism by which I could write the callee:
sub retrieve_or_calculate (Key $key, Hashlike $cache)
{
$cache->{$key} //= calculate_expensive_value($key);
return $cache->{key};
}
... then on the caller side I could provide anything which performs the Key
and Hashlike
roles and the code would work as expected. Obviously I'd have to do a little bit of work on the caller side to ensure that if I passed anything other than a simple value for $key
and a hash reference for $cache
that they had the appropriate annotations to mark that they performed the appropriate roles... but the obvious code you'd write by default without doing anything spectacular could work without modification.
Don't get caught up in the function signatures or the implicit compiler-provided error checking. Think about how this imaginary feature not present in Perl 5 has removed the need to guess from both sides of this call. Of course it would be optional. Of course you don't have to use it. (Of course it implies the presence of other features, which I'll discuss in my next entry.) Of course you can write copious tests to prove that this isn't a problem in practice for every use case you imagined when you wrote the tests.
Yet look at how it solves a very real robustness and correctness problem in existing Perl 5 code. Perl 6 supports this feature for a very good reason. (You can use it today in Rakudo.)
If none of this convinces you, MJD as usual explains things better than I do. See Strong Typing Doesn't Have to Suck (1999) and his Atypical Types from OOPSLA 2008.
Did you check http://search.cpan.org/~flora/MooseX-MultiMethods-0.10/lib/MooseX/MultiMethods.pm ?
The problem is not solved by MX::MultiMethods, nor can it be solved by anything on CPAN.
Without having the core language provide a set of roles for primitives (like HashLike, ArrayLike, IntLike, etc) there's no standard for what types to use. Without a standard, all we have are the same ad-hoc checks that chromatic has already outlined.
It's possible that a standard could emerge on CPAN, but this is really something that belongs in the core.
Sounds oddly like Haskell type classes.
(Saying this as a Perl addict, of course. Haskell's type system makes it seriously hard to do rapid development.)