Take me over?
NAME
Method::Signatures - method declarations with signatures and no source filter
SYNOPSIS
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;
}
DESCRIPTION
This is ALPHA SOFTWARE which relies on YET MORE ALPHA SOFTWARE. Use at your own risk. Features may change.
Provides a proper method keyword, like "sub" but specificly for making methods. Also allows signatures. Finally it will automatically provide the invocant as $self
. No more my $self = shift
.
And it does all this with no source filters.
Signature syntax
method foo($bar, $baz) {
$self->wibble($bar, $baz);
}
is equivalent to:
sub foo {
my $self = shift;
my($bar, $baz) = @_;
$self->wibble($bar, $baz);
}
except the original line numbering is preserved.
No checks are made that the arguments being passed in match the signature.
Future releases will add extensively to the signature syntax probably along the lines of Perl 6.
@_
Other than removing $self
, @_
is left intact. You are free to use @_
alongside the arguments provided by Method::Signatures.
Named parameters
Parameters can be passed in named, as a hash, using the :$arg
syntax.
method foo(:$arg) {
...
}
Class->foo( arg => 42 );
Named parameters are optional and can have defaults.
Aliased references
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.
package Stuff;
method add_one(\@foo) {
$_++ for @foo;
}
my @bar = (1,2,3);
Stuff->add_one(\@bar); # @bar is now (2,3,4)
Invocant parameter
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:
.
Defaults
Each parameter can be given a default with the $arg = EXPR
syntax. For example,
method add($this = 23, $that = 42) {
return $this + $that;
}
Defaults will only be used if the argument is not passed in at all. Passing in undef
will override the default. That means...
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.
Parameter traits
Each parameter can be assigned a trait with the $arg is TRAIT
syntax.
method stuff($this is ro) {
...
}
Any unknown trait is ignored.
Most parameters have a default traits of is rw is copy
.
- ro
-
Read-only. Assigning or modifying the parameter is an error.
- rw
-
Read-write. It's ok to read or write the parameter.
This is a default trait.
- copy
-
The parameter will be a copy of the argument (just like
<my $arg = shift
>).This is a default trait except for the
\@foo
parameter. - alias
-
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.
Traits and defaults
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.
Optional parameters
To declare a parameter optional, use the $arg?
syntax.
Currently nothing is done with this. It's for forward compatibility.
Required parameters
To declare a parameter as required, use the $arg!
syntax.
All parameters without defaults are required by default.
The @_
signature
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.
Anonymous Methods
An anonymous method can be declared just like an anonymous sub.
my $method = method ($arg) {
return $self->foo($arg);
};
$obj->$method(42);
PERFORMANCE
There is no run-time performance penalty for using this module above what it normally costs to do argument handling.
DEBUGGING
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).
EXAMPLE
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;
...
}
EXPERIMENTING
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.
This interface is experimental, unstable and will change between versions.
BUGS, CAVEATS and NOTES
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.
method
is not declared at compile time.
Unlike declaring a sub
, method
currently does not happen at compile time. This usually isn't a problem. It may change, but it may be a good thing.
Debugging
This totally breaks the debugger. Will have to wait on Devel::Declare fixes.
You can see the Perl code Method::Signatures translates to by using B::Deparse.
One liners
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.
No source filter
While this module does rely on the hairy black magic of Devel::Declare and Data::Alias 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.
What about regular subroutines?
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
.
What about class methods?
Right now there's nothing special about class methods. Just use $class
as your invocant like the normal Perl 5 convention.
There may be special syntax to separate class from object methods in the future.
What about types?
I would like to add some sort of types in the future or simply make the signature handler pluggable.
What about the return value?
Currently there is no support for types or declaring the type of the return value.
How does this relate to Perl's built-in prototypes?
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($;$)
Error checking
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.
What about...
Named parameters are in the pondering stage.
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)
THANKS
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 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.
LICENSE
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
SEE ALSO
MooseX::Method::Signatures, Perl6::Signature, Sub::Signatures, Perl6::Subs
Perl 6 subroutine parameters and arguments - http://perlcabal.org/syn/S06.html#Parameters_and_arguments