New Method::Signatures: better attributes, traits, aliases

schwern on 2008-10-06T07:13:34

New release of Method::Signatures is on its way to CPAN. I'm in a sprint to get all the sizzle implemented before PPW.

This release features improved parsing of method attributes, thanks to stealing code from Florian Ragwitz's MooseX::Method::Signatures.

Some parameter traits have been implemented. "is alias" lets you work with the parameter as an alias, altering it in the caller's namespace. Previously this was only possible by manipulating @_ directly.

These two are equivalent.

sub strip_ws {
    my $self = shift;
    $_[0] =~ s/^\s+//;
    $_[0] =~ s/\s+$//;
    return;
}

method strip_ws($str is alias) { $str =~ s/^\s+//; $str =~ s/\s+$//; return; }

my $str = " support the right to arm bears "; Class->strip_ws($str); # works in place on $str


During the initial arguments about how signatures should work on p5p one of the longest and loudest arguments was whether or not they should copy or alias by default. Perl 6 aliases, but Perl 6 is an "everything is an object" language which passes by reference all over the place. Perl 5 is not. Like it or not, Perl 5 copies. So copying is the default. But every once in a while you want aliasing, so here it is.

"is ro" makes a parameter read only (using the Readonly module). Now you can apply some strictness to your subroutines.

method echo($msg is ro) {
    return $msg;
}


If echo() tried to alter $msg it would throw an error.

Required and optional parameters are finally implemented. All positional parameters are required by default. Parameters with defaults are optional.

# $x and $y are required.  $z is optional and defaults to 0
method print_position($x, $y, $z=0) {
    print "$x, $y, $z";
}

# $url is required. $user and $pass are optional. method get($url, $user?, $pass?) { ... }


The code generated for the required argument check is redundant (use B::Deparse to see it) and could be tightened up to improve performance. If someone wants to try out some relatively low hanging fruit, that's it. It's on Github so feel free to make a fork and work on it.

Next version will implement named parameters. After talking it over with Florian I think I'm going to go with his semantics from MooseX::Method::Signatures. Named parameters are declared with :$foo as in Perl 6. They are optional by default. Positional parameters can only be called positionally. Named parameters can only be called named. Disallowing mixing resolves ambiguities that Perl 6 needs a heap of special syntax to resolve.

# year is required.  The rest are optional and have defaults.
# They are all readonly.
method date2epoch(:$year! is ro,  :$month = 1 is ro, :$day = 1 is ro,
                  :$hour = 0 is ro, :$min = 0 is ro, :$sec = 0 is ro) {
    ...
}

# Jan 1st, 2008 00:00:00 my $epoch = Date->date2epoch( year => 2008 );

# One minute after midnight my $epoch = Date->date2epoch( year => 2008, min => 1 );

# $text is positional, the rest are named and optional method format($text, :$justify = "left", :$quality) { ... }

# left justified Class->format("mu");

# right justified, Livejournal quality poetry. Class->format(<<'POETRY', justify => "right", quality => "LJ"); ... POETRY


Just how much code does this save? Here's date2epoch() written out long-hand.

sub date2epoch {
    my $self = shift;
    my %args = @_;

croak "date2epoch() missing required argument \$year" unless exists $args{year};

Readonly my $year = $args{year}; Readonly my $month = $args{month} || 1; Readonly my $day = $args{day} || 1; Readonly my $hour = $args{hour} || 0; Readonly my $min = $args{min} || 0; Readonly my $sec = $args{sec} || 0;

... }


Wow, even I'm surprised! Do you really need all that? No, you could do without Readony and you could leave everything in %args. But with Method::Signatures you no longer need to compromise with verbosity. You can have all the features and compact code.


Looks good!

deepfryed on 2008-10-06T08:46:05

wow, looks really good. Looking forward to seeing this on CPAN.