Method::Signatures - method and function declarations with signatures and no source filter
package Foo; use Method::Signatures; method new (%args) { return bless {%args}, $self; } method get ($key) { return $self->{$key}; } method set ($key, $val) { return $self->{$key} = $val; } func hello($greeting, $place) { print "$greeting, $place!\n"; }
Provides two new keywords, func and method so you can write subroutines with signatures instead of having to spell out my $self = shift; my($thing) = @_
func
method
my $self = shift; my($thing) = @_
func is like sub but takes a signature where the prototype would normally go. This takes the place of my($foo, $bar) = @_ and does a whole lot more.
sub
my($foo, $bar) = @_
method is like func but specificly for making methods. It will automatically provide the invocant as $self. No more my $self = shift.
$self
my $self = shift
Also allows signatures, very similar to Perl 6 signatures.
And it does all this with no source filters.
func echo($message) { print "$message\n"; }
is equivalent to:
sub echo { my($message) = @_; print "$message\n"; }
except the original line numbering is preserved and the arguments are checked to make sure they match the signature.
Similarly
method foo($bar, $baz) { $self->wibble($bar, $baz); }
sub foo { my $self = shift; my($bar, $baz) = @_; $self->wibble($bar, $baz); }
@_
Other than removing $self, @_ is left intact. You are free to use @_ alongside the arguments provided by Method::Signatures.
Parameters can be passed in named, as a hash, using the :$arg syntax.
:$arg
method foo(:$arg) { ... } Class->foo( arg => 42 );
Named parameters by default are optional.
Required positional parameters and named parameters can be mixed, but the named params must come last.
method foo( $a, $b, :$c ) # legal
Named parameters are passed in as a hash after all positional arguments.
method display( $text, :$justify = 'left', :$enchef = 0 ) { ... } # $text = "Some stuff", $justify = "right", $enchef = 0 $obj->display( "Some stuff", justify => "right" );
You cannot mix optional positional params with named params as that leads to ambiguities.
method foo( $a, $b?, :$c ) # illegal # Is this $a = 'c', $b = 42 or $c = 42? $obj->foo( c => 42 );
A signature of \@arg will take an array reference but allow it to be used as @arg inside the method. @arg is an alias to the original reference. Any changes to @arg will effect the original reference.
\@arg
@arg
package Stuff; method add_one(\@foo) { $_++ for @foo; } my @bar = (1,2,3); Stuff->add_one(\@bar); # @bar is now (2,3,4)
This feature requires Data::Alias to be installed. Method::Signatures does not depend on it because it does not currently work after 5.10.
The method invocant (ie. $self) can be changed as the first parameter. Put a colon after it instead of a comma.
method foo($class:) { $class->bar; } method stuff($class: $arg, $another) { $class->things($arg, $another); }
Signatures have an implied default of $self:.
$self:
Each parameter can be given a default with the $arg = EXPR syntax. For example,
$arg = EXPR
method add($this = 23, $that = 42) { return $this + $that; }
Almost any expression can be used as a default.
method silly( $num = 42, $string = q[Hello, world!], $hash = { this => 42, that => 23 }, $code = sub { $num + 4 }, @nums = (1,2,3), ) { ... }
Defaults will only be used if the argument is not passed in at all. Passing in undef will override the default. That means...
undef
Class->add(); # $this = 23, $that = 42 Class->add(99); # $this = 99, $that = 42 Class->add(99, undef); # $this = 99, $that = undef
Earlier parameters may be used in later defaults.
method copy_cat($this, $that = $this) { return $that; }
All variables with defaults are considered optional.
Each parameter can be assigned a trait with the $arg is TRAIT syntax.
$arg is TRAIT
method stuff($this is ro) { ... }
Any unknown trait is ignored.
Most parameters have a default traits of is rw is copy.
is rw is copy
Read-only. Assigning or modifying the parameter is an error.
Read-write. It's ok to read or write the parameter.
This is a default trait.
The parameter will be a copy of the argument (just like <my $arg = shift>).
<my $arg = shift
This is a default trait except for the \@foo parameter.
\@foo
The parameter will be an alias of the argument. Any changes to the parameter will be reflected in the caller.
This is a default trait for the \@foo parameter.
To have a parameter which has both a trait and a default, set the trait first and the default second.
method echo($message is ro = "what?") { return $message }
Think of it as $message is ro being the left-hand side of the assignment.
$message is ro
To declare a parameter optional, use the $arg? syntax.
$arg?
Currently nothing is done with this. It's for forward compatibility.
To declare a parameter as required, use the $arg! syntax.
$arg!
All parameters without defaults are required by default.
The @_ signature is a special case which only shifts $self. It leaves the rest of @_ alone. This way you can get $self but do the rest of the argument handling manually.
An anonymous method can be declared just like an anonymous sub.
my $method = method ($arg) { return $self->foo($arg); }; $obj->$method(42);
Method::Signatures is mostly a straight subset of Perl 6 signatures. The important differences...
As noted above, there are more restrictions on named parameters than in Perl 6.
Perl 5 lacks all the fancy named parameter syntax for the caller.
In Perl 6, parameters are aliases. This makes sense in Perl 6 because Perl 6 is an "everything is an object" language. In Perl 5 is not, so parameters are much more naturally passed as copies.
You can alias using the "alias" trait.
Perl 6 allows you to use any parameter as a named parameter. Perl 5 lacks the named parameter disambiguating syntax so it is not allowed.
Because in Perl 6 arrays and hashes don't get flattened, and their referencing syntax is much improved. Perl 5 has no such luxury, so Method::Signatures added a way to alias references to normal variables to make them easier to work with.
Method::Signatures lets you punt and use @_ like in regular Perl 5.
There is no run-time performance penalty for using this module above what it normally costs to do argument handling.
One of the best ways to figure out what Method::Signatures is doing is to run your code through B::Deparse (run the code with -MO=Deparse).
Here's an example of a method which displays some text and takes some extra options.
use Method::Signatures; method display($text is ro, :$justify = "left", :$fh = \*STDOUT) { ... } # $text = $stuff, $justify = "left" and $fh = \*STDOUT $obj->display($stuff); # $text = $stuff, $justify = "left" and $fh = \*STDERR $obj->display($stuff, fh => \*STDERR); # error, missing required $text argument $obj->display();
The display() method is equivalent to all this code.
sub display { my $self = shift; croak('display() missing required argument $text') unless @_ > 0; Readonly my $text = $_[0]; my(%args) = @_[1 .. $#_]; my $justify = exists $args{justify} ? $args{justify} : 'left'; my $fh = exists $args{fh} ? $args{'fh'} : \*STDOUT; ... }
If you want to experiment with the prototype syntax, replace Method::Signatures::make_proto_unwrap. It takes a method prototype and returns a string of Perl 5 code which will be placed at the beginning of that method.
Method::Signatures::make_proto_unwrap
This interface is experimental, unstable and will change between versions.
Please report bugs and leave feedback at <bug-Method-Signatures> at <rt.cpan.org>. Or use the web interface at http://rt.cpan.org. Report early, report often.
You can see the Perl code Method::Signatures translates to by using B::Deparse.
If you want to write "use Method::Signatures" in a one-liner, do a -MMethod::Signatures first. This is due to a bug/limitation in Devel::Declare.
-MMethod::Signatures
While this module does rely on the black magic of Devel::Declare to access Perl's own parser, it does not depend on a source filter. As such, it doesn't try to parse and rewrite your source code and there should be no weird side effects.
Devel::Declare only effects compilation. After that, it's a normal subroutine. As such, for all that hairy magic, this module is surprisingly stable.
Devel::Declare cannot yet change the way sub behaves. It's being worked on and when it works I'll release another module unifying method and sub.
I might release something using func.
Right now there's nothing special about class methods. Just use $class as your invocant like the normal Perl 5 convention.
$class
There may be special syntax to separate class from object methods in the future.
I would like to add some sort of types in the future or simply make the signature handler pluggable.
Currently there is no support for types or declaring the type of the return value.
It doesn't. Perl prototypes are a rather different beastie from subroutine signatures. They don't work on methods anyway.
A syntax for function prototypes is being considered.
func($foo, $bar?) is proto($;$)
There currently is very little checking done on the prototype syntax. Here's some basic checks I would like to add, mostly to avoid ambiguous or non-sense situations.
* If one positional param is optional, everything to the right must be optional
method foo($a, $b?, $c?) # legal method bar($a, $b?, $c) # illegal, ambiguous
Does <-bar(1,2)>> mean $a = 1 and $b = 2 or $a = 1, $c = 3?
<-
* If you're have named parameters, all your positional params must be required.
method foo($a, $b, :$c); # legal method bar($a?, $b?, :$c); # illegal, ambiguous
Does <-bar(c => 42)>> mean $a = 'c', $b = 42 or just $c = 42?
* Positionals are resolved before named params. They have precedence.
Method traits are in the pondering stage.
An API to query a method's signature is in the pondering stage.
Now that we have method signatures, multi-methods are a distinct possibility.
Applying traits to all parameters as a short-hand?
# Equivalent? method foo($a is ro, $b is ro, $c is ro) method foo($a, $b, $c) is ro
A "go really fast" switch. Turn off all runtime checks that might bite into performance.
Method traits.
method add($left, $right) is predictable # declarative method add($left, $right) is cached # procedural # (and Perl 6 compatible)
Most of this module is based on or copied from hard work done by many other people.
All the really scary parts are copied from or rely on Matt Trout's, Florian Ragwitz's and Rhesa Rozendaal's Devel::Declare work.
The prototype syntax is a slight adaptation of all the excellent work the Perl 6 folks have already done.
Also thanks to Matthijs van Duin for his awesome Data::Alias which makes the \@foo signature work perfectly and Sub::Name which makes the subroutine names come out right in caller().
And thanks to Florian Ragwitz for his parallel MooseX::Method::Signatures module from which I borrow ideas and code and Devel::BeginLift which lets the methods be declared at compile time.
The original code was taken from Matt S. Trout's tests for Devel::Declare.
Copyright 2007-2008 by Michael G Schwern <schwern@pobox.com>.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
See http://www.perl.com/perl/misc/Artistic.html
MooseX::Method::Signatures for a method keyword that works well with Moose.
Perl6::Signature for a more complete implementation of Perl 6 signatures.
Method::Signatures::Simple for a more basic version of what Method::Signatures provides.
signatures for sub with signatures.
Perl 6 subroutine parameters and arguments - http://perlcabal.org/syn/S06.html#Parameters_and_arguments
To install Method::Signatures, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Method::Signatures
CPAN shell
perl -MCPAN -e shell install Method::Signatures
For more information on module installation, please visit the detailed CPAN module installation guide.