http://qs321.pair.com?node_id=412554

If you're familiar with modern programming languages other than Perl, you may be frustrated with Perl's relatively simple argument handling. While it's very easy to understand, it leads to a wide variety of bugs that are far less common in other languages. Consider the following example of overloading a method to be both a setter and a getter (a common idiom in Perl):

sub name { my $self = shift; if (@_) { $self->{name} = shift; return $self; } return $self->{name}; }

The intent is to allow the programmer to do this:

$person->name; # get the name $person->name("Ovid"); # set the name

Superficially, this looks fine. However, this code throws away information about the arguments which in turn leads to more bugs. What happens when the person using this code tries the following:

$person->name(qw/Publius Ovidius Naso/); # or $person->name([qw/Publius Ovidius Naso/]); # or my $name = Name->new("Publius Ovidius Naso"); $person->name($name);

With the code as written, all of those method calls would "succeed" and silently DWIDM (do what I don't mean.) This is a pernicious source of bugs. In fact, there's a lot of debate in the Perl community about whether or not it's worth putting in a lot of extra checks in the code to protect against this.

sub name { my $self = shift; if (1 == @_ && ! ref $_[0]) { $self->{name} = shift; return $self; } elsif (! @_) { return $self->{name}; } else { croak "Unknown arguments to name()"; } }

Wow. Our nice, clean, easy to read subroutine is starting to turn into an ugly mess. That's one of the reasons I've favored separate setters and getters, but what I would really like to do is this (available in just about any modern programming language):

sub name($self) { return $self->{name}; } sub name($self, $name) { $self->{name} = $name; return $self; }

Wouldn't that be nice? It would be so much easier to program that way. Well, now you can. I've uploaded Sub::Signatures to the CPAN.

It allows you to specify method and function signatures and dispatch accordingly. In 'loose' mode (the default), it merely dispatches based on the number of arguments:

use Sub::Signatures; sub foo($bar) { print $bar; } sub foo($bar, $baz) { print "$baz, $bar"; } foo(1); # prints 1 foo(1,2); # prints 1, 2 foo(1,2,3); # fatal error

In 'strict' mode, it requires that functions/methods be "typed" according to what ref would return. If ref returns nothing, then SCALAR is assumed. If not type is specified, SCALAR is assumed. That, by the way, is why the &name examples above do what I meant. In strict mode, with no type specified, the $name argument is required to be a SCALAR. Attempting to assign a Name object is a fatal error.

Currently supported features:

  • Methods
  • Subroutines
  • Optional strong typing via the ref function
  • Exporting
  • Inheritance
  • Useful error message

And in version 0.1, currently on its way to the CPAN, I've added support for anonymous functions and halting compilation for duplicate signatures.

Yes, this is alpha code and yes, it's a source filter. If your particular brand of dogmatism says "no source filters" that's fine, but do read this first. If you still don't want to use this, I won't be offended.

If you do want this, please give me feedback, suggestions, bug reports, etc. I would like to get this "production ready" if possible.

Cheers,
Ovid

New address of my CGI Course.