Perl6::Subs - Define your subroutines in the Perl 6 style
Version 0.05
use Perl6::Subs; sub foo ($x) # Positional parameters { bar($x) } sub get (Array $x) # Type validation { pop @$x } sub get_or_die ($x of Array where { @$_ }) # Subtyping { pop @$x } sub show (Str $s, IO ?$io) # Optional parameters { print { $io || *STDOUT } $s } sub limit (Int $i, Int +$hi, Int +$low) # Named parameters { ... } method foo # Invocant: '$self' { $self->bar } method foo (Foo $self: Bar $x) # Parameter 'isa' classname { $self->use_bar($x) }
And there's more...
Perl6::Subs is a source filter that adds a very useful subset of Perl 6 subroutine syntax to Perl 5. Given a subroutine defined with a Perl 6 prototype, the code generated by Perl6::Subs will, at runtime, declare the formal parameters, assign them their values, and validate their contents according to both built-in and user-given rules.
Perl6::Subs supports all five categories of Perl 6 subroutine parameters, here listed in their mandatory order. (You may skip categories, but not reorder them.)
Method invocant, e.g. $self:). Invocant declarations are marked by a trailing colon rather than the usual comma. To declare an invocant when there are no other parameters, just end the prototype with the colon.
$self:
Mandatory positional, e.g. $foo.
$foo
Optional positional, e.g. ?$foo.
?$foo
Optional named, e.g. +$foo.
+$foo
Required named, e.g. +$foo is required.
+$foo is required
Slurpy, e.g. *@rest or *%rest. Slurpy parameters must appear last, as they consume all remaining parameter values.
*@rest
*%rest
Perl 5's limited function call semantics (the single array @_) prevent Perl6::Subs from supporting all the features of Perl 6 parameter lists. A given subroutine can have either optional positional parameters or named parameters or a slurpy parameter; combining them is illegal. As the lone exception, a subroutine with named parameters may also have a slurpy hash; the hash will contain all the key/value pairs not explicitly given as named parameters.
Perl6::Subs understands the following type names, and will generate code that validates at runtime that a declared parameter's value actually has the given type, and throws an exception if it fails.
First, the fundamental Perl 6 types, which in Perl 6 will be unboxed (non-objects):
Any defined scalar. (Note: References are permitted. Given Perl 5's overloading facility, a reference may actually have a useful string value if you go ahead and use it.) Due to the semantics of Perl 6's str type, use of str is recommended only for bare byte buffers without string semantics.
str
A number; specifically a defined scalar that passes the test of Scalar::Util::looks_like_number.
Scalar::Util::looks_like_number
An integer; specifically, a num with no fractional part.
Any reference value.
Any defined scalar.
Now, the object types. Note that while Perl 6 considers all these to be objects, Perl 5 often doesn't. Also note that, in general, undef is permitted as a valid value for all object types.
object types
undef
Any value, including undef.
Any scalar value, including undef. (In Perl 5, this is a synonym for Any.)
A num, or undef.
An int, or undef.
A reference value, or undef.
An array reference, or undef.
A hash reference, or undef.
A code (subroutine) reference, or undef.
A regexp reference (qr//), or undef.
A value that can be used for I/O: An IO handle reference (e.g. *STDOUT{IO}, a glob (e.g. *STDOUT), a glob reference (e.g. \*STDOUT), or undef. Note that autovivified file handles are legal IO values, as they are references to (blessed) globs.
*STDOUT{IO}
*STDOUT
\*STDOUT
IO
Perl6::Subs also supports these type names which are not legal in Perl 6, but which may be useful in writing Perl 5:
A glob value (e.g. *STDOUT), or undef.
A glob reference (e.g. \*STDOUT), or undef.
Finally, any bareword other than the above used as a type name is assumed to name a user-defined class. A parameter so typed must satisfy UNIVERSAL::isa() of that class, or be undef. If Perl warnings are enabled at compile time, a mispelled class name will generate a diagnostic.
Perl6::Subs filters subroutines declared either with a Perl 6 style prototype; or with the "method" keyword replacing the word "sub" (in which case the "method" trait is implied). Perl6::Subs also understand both Perl 5 and Perl 6 syntax for subroutine traits (Perl 5 calls them "attributes"). Thus, these declarations are synonymous:
sub foo is method {...} sub foo returns(Any) is method {...} sub foo ($self:) : method {...} method foo {...} method foo ($self:) {...}
However, this declaration uses no Perl 6 features, and therefore Perl6::Subs does not filter it:
sub foo : method {...}
Perl6::Subs understands subroutine traits after the prototype declared with three syntax flavors:
Perl 6 standard trait syntax, e.g. is trait and is trait(params)
is trait
is trait(params)
Perl 5 subroutine attributes, introduced with a colon, e.g. :method
:method
As a special case, the Perl 6 trait returns(TYPE), which requires neither is nor a colon.
returns(TYPE)
Perl6::Subs supports a subset of the Perl 6 type declaration syntax. Allowed parameter forms are Type $var and $var of Type. Thus,
Type $var
$var of Type
sub foo (Int $i)
and
sub foo ($i of Int)
are synonymous. Any parameter traits must be specified at the end of the given declaration. Thus a required named parameter of type Int may be specified as:
sub foo (Int +$i is required)
or
sub foo (+$i of Int is required)
(The only implemented parameter trait as of this writing is is required, which is only meaningful on named parameters.)
is required
You may create an anonymous subtype (restricted type) using a where clause, which specifies a block of code that must evaluate to true when $_ is set to a given value. For example, to accept only positive integers as parameters, you could write:
where
$_
sub foo (Int where { $_ > 0 } $i)
sub foo ($i of Int where { $_ > 0 })
(The latter is clearer, in the author's opinion, since the variable is textually closer to the base type.)
If the environment variable PERL6_SUBS_DEBUG is set to a true value, Perl6::Subs will print to standard output the complete filtered text of any source file in which it is used.
PERL6_SUBS_DEBUG
If you're debugging Perl6::Subs itself, the environment variables PERL6_SUBS_RD_TRACE and PERL6_SUBS_RD_HINTS set the $::RD_TRACE and $::RD_HINTS variables, respectively, opening a window on the operation of Parse::RecDescent.
PERL6_SUBS_RD_TRACE
PERL6_SUBS_RD_HINTS
$::RD_TRACE
$::RD_HINTS
Parse::RecDescent
Do not use parameter names that turn into Perl quoting operators when their sigils are stripped: "$y", "$m", "@tr", "@q", etc. If you do so, Filter::Simple will be fooled into thinking large parts of your program are quoted strings, and large parts of your code may go unfiltered.
is copy
In Perl 6, formal parameters are usually read-only aliases for the actual parameter values. Parameters with the is rw trait are writeable aliases, and parameters with the is copy trait are writeable copies.
is rw
Perl6::Subs defaults to is copy semantics for efficiency reasons. Given that we're working on top of Perl 5, this is unlikely to change. Read-only aliasing is not a Perl 5 feature; to provide its semantics would currently require tying, and that's just too slow for us to make it the default. On the other hand, support for is rw may someday be provided via Lexical::Alias.
The Perl 5 code produced by this source filter never includes Perl 5 prototypes for the functions with Perl 6 prototypes. This is a design decision driven mostly by its intended usage: creating object-oriented Perl code. Method calls in Perl 5 always ignore prototypes. And if you don't know what a Perl5 prototype is, exactly, defining it can be a tricky thing.
I suspect that if we ever add this feature, it will be as an additional function attribute:
sub foo (Int $x) is perl5_prototype($) { ... }
Some missing Perl 6 features that would be helpful in Perl 5 code:
Junction types (e.g. Foo | Bar)
Foo | Bar
Default parameter values (e.g. Int +$pos = 0)
Int +$pos = 0
Parameter traits and the features they enable (e.g. Int +$pos is required. (Perl6::Subs doe parse parameter traits, but it then proceeds to ignore them.)
Int +$pos is required
Perl 6 prototypes on sub declarations (as opposed to definitions).
Interspersed comments in sub definitions. If you include any comments from the "sub" keyword to the open brace of the sub body, bad things will happen. This bug should be easy to fix by using the "placeholder" regular expression provided so kindly by Damian.
The default invocant for methods is $self, because that's the convention used by about 90% of object-oriented code on CPAN. (The actual default invocant for Perl 6 is somewhat up in the air.)
$self
The default invocant for Perl6::Subs should probably be user-changable. But I'm not sure the best user interface. This would work:
use Perl6::Subs invocant => '$this';
And yet, would you really want to have to put that at the top of every module? You'd probably just start using $self. For a large project, something like this would be better:
# MyProject.pm package MyProject; use Perl6::Subs::Defaults invocant => '$this';
There's a hook for support of Params::Validate: valid {}, which works syntatically just like where {}. As of this writing, it does nothing.
valid {}
where {}
This module is a source filter. Source filters always break. For example, the breakage caused by parameter names that turn into Perl quoting operators when their sigils are stripped may never be fixed.
Please report any other bugs or feature requests to bug-perl6-subs@rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Perl6-Subs. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
bug-perl6-subs@rt.cpan.org
Chip Salzenberg, <chip@pobox.com>
<chip@pobox.com>
Thanks to Heath Market Science <hmsonline.com> for funding creation of this module. Thanks also to Larry, Damian, Allison, et al for Perl 6 subroutine syntax, and to Damian for Filter::Simple and Parse::RecDescent.
Copyright 2005 Chip Salzenberg and Health Market Science.
This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
To install Perl6::Subs, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Perl6::Subs
CPAN shell
perl -MCPAN -e shell install Perl6::Subs
For more information on module installation, please visit the detailed CPAN module installation guide.