I've released the newest version of Sub::Signatures. As the previous version was labeled an alpha and I warned that the interface could change, I didn't feel too bad about removing support for typed arguments (though with a loud warning in the docs). My reasoning goes like this (cribbed from the docs):
The first version of Sub::Signatures allowed optional strong typing by letting you specify the exact ref type each argument should be:
sub foo (ARRAY $bar, HASH $baz, CGI $query) { ... }
Why did this go away? There were several problems. First, specifying the exact data type meant that isa relationships were ignored. This would be unfortunate if we wanted to pass in a subclass of something. That leads to the second problem.
If we were to check isa relationships, this sometimes leads to problems with ambiguous method resolution. Witness the debates on the Perl 6 language list about the merits of "Manhattan distance" in MMD resolution and you quickly get an idea of what I mean. However, for me it boils down to this. Imagine the following hierarchy:
Mammal / \ / \ Dog Cat
Now imagine the two following methods:
sub foo ($self, Dog $dog, Mammal $mammal) {...} sub foo ($self, Mammal $mammal, Cat $cat {...}
So we call one of those methods:
$objet->foo($dog, $cat);
Which method should get called? Should it depend on the order the methods were declared? Should we die with an "ambiguous method call" message despite the fact that one of those methods might be able to work? Do we offer extra syntax (shudder) stating the method precedence? Trying to properly resolve MMD issues, particularly in a Pure Perl module is merely going to slow things down. This, however, leads to the third and final reason why I had to remove this.
sub foo (CGI $query) { ... }
The real nail in the coffin was that CGI $query parameter above. What if we actually have a CGI::Simple object passed in instead? It almost completely conforms to the CGI interface. If it does what we want, the type checking guarantees that that this method will fail for no good reason.
However, no argument checking is a bad thing. What we're really interested in is whether or not a given argument is capable of providing what we need, not whether or not it's a given type. This puts your author in a bind. Objects which are unrelated by inheritance but present the same behaviors are allomorphic (or perhaps, more properly, we can say they present allomorphic interfaces?).
Allomorphism, despite the funny name, is something Perl programmers use all the time without being aware of what it's called. However, to add allomorphism support to this module would complicate it quite a bit. I'd have to come up with even more syntax and my primary goal was to keep this module as simple as possible. To achieve this we restrict ourselves to dispatching on the number of arguments. Thus, you, the programmer, will still need to validate the different types and/or capabilities of the arguments you pass in.
I can only imagine how true language designers lie awake at night worrying over every little thing. When I have something this simple being perplexing, I can't imagine building an entire language.
On the plus side, the new Sub::Signatures allows optional "fallback" methods. Before, if you overloaded a method, you had to absolutely handle every number of arguments.
use Sub::Signatures; sub foo($bar) { print "$bar\n"; } sub foo($bar, $baz) { print "$bar, $baz\n"; } foo(1); # prints 1 foo(2,3); # prints 2, 3 foo(2,3,4); # fatal runtime error
That's handy, but what if you really want to be able to pass in a list? For example:
use Sub::Signatures qw/methods/; sub name($self) { return $self->{name}; } sub name($self, $name) { $self->{name} = shift; return $self; }
But what if you want to allow this?
sub name { my $self = shift; return $self->{name} unless @_; $self->{name} = join ' ', @_; return $self; }
Previously, Sub::Signatures would not allow that because it could not handle an arbitrary number of arguments. Now, however, you can do this:
sub name (fallback) { my $self = shift; $self->{name} = join ' ', @_; return $self; }
First it will look to see if you have any sub/methods with the number of parameters you have passed. If not, instead of just a runtime error, it will allow you to name a "fallback" method which behaves like a normal Perl subroutine.
I could have coded it to simply allow you to leave off the (fallback) and have any such subroutine automatically serve as the fallback. I decided not to for two reasons. First, it was simpler this way and I want this code to be so simple that it's very hard to break. Second, if a maintenance programmer sees the fallback declaration, he or she knows that this is probably an overridden subroutine and this will make debugging easier. It never hurts to throw a bone to the maintenance programmers.