use XSLoader;
BEGIN {
- our $VERSION = '0.06';
+ our $VERSION = '0.10_01';
XSLoader::load;
}
name => 'optional',
default_arguments => 1,
check_argument_count => 0,
+ named_parameters => 1,
},
method => {
name => 'optional',
default_arguments => 1,
check_argument_count => 0,
+ named_parameters => 1,
attrs => ':method',
shift => '$self',
+ invocant => 1,
},
classmethod => {
name => 'optional',
default_arguments => 1,
check_argument_count => 0,
+ named_parameters => 1,
attributes => ':method',
shift => '$class',
+ invocant => 1,
},
);
+for my $k (keys %type_map) {
+ $type_map{$k . '_strict'} = {
+ %{$type_map{$k}},
+ check_argument_count => 1,
+ };
+}
sub import {
my $class = shift;
- @_ or @_ = {
- fun => 'function',
- method => 'method',
- };
+ if (!@_) {
+ @_ = {
+ fun => 'function',
+ method => 'method',
+ };
+ }
+ if (@_ == 1 && $_[0] eq ':strict') {
+ @_ = {
+ fun => 'function_strict',
+ method => 'method_strict',
+ };
+ }
if (@_ == 1 && ref($_[0]) eq 'HASH') {
- @_ = map [$_, $_[0]{$_}], keys %{$_[0]}
- or return;
+ @_ = map [$_, $_[0]{$_}], keys %{$_[0]};
}
my %spec;
: 1
;
$clean{check_argument_count} = !!delete $type{check_argument_count};
+ $clean{invocant} = !!delete $type{invocant};
+ $clean{named_parameters} = !!delete $type{named_parameters};
%type and confess "Invalid keyword property: @{[keys %type]}";
;
$flags |= FLAG_DEFAULT_ARGS if $type->{default_arguments};
$flags |= FLAG_CHECK_NARGS if $type->{check_argument_count};
+ $flags |= FLAG_INVOCANT if $type->{invocant};
+ $flags |= FLAG_NAMED_PARAMS if $type->{named_parameters};
$^H{HINTK_FLAGS_ . $kw} = $flags;
$^H{HINTK_SHIFT_ . $kw} = $type->{shift};
$^H{HINTK_ATTRS_ . $kw} = $type->{attrs};
}
# function with prototype
- fun mymap($fun, @args) :(&@) {
+ fun mymap($fun, @args)
+ :(&@)
+ {
my @res;
for (@args) {
push @res, $fun->($_);
method set_name($name) {
$self->{name} = $name;
}
-
+
+ # method with explicit invocant
+ method new($class: %init) {
+ return bless { %init }, $class;
+ }
+
# function with default arguments
fun search($haystack, $needle = qr/^(?!)/, $offset = 0) {
...
}
-
+
# method with default arguments
method skip($amount = 1) {
$self->{position} += $amount;
=pod
+ use Function::Parameters qw(:strict);
+
+ fun greet($x) {
+ print "Hello, $x\n";
+ }
+
+ greet "foo", "bar";
+ # Dies at runtime with "Too many arguments for fun greet"
+
+ greet;
+ # Dies at runtime with "Not enough arguments for fun greet"
+
+=cut
+
+=pod
+
# use different keywords
use Function::Parameters {
proc => 'function',
This module lets you use parameter lists in your subroutines. Thanks to
L<PL_keyword_plugin|perlapi/PL_keyword_plugin> it works without source filters.
-WARNING: This is my first attempt at writing L<XS code|perlxs> and I have
-almost no experience with perl's internals. So while this module might
-appear to work, it could also conceivably make your programs segfault.
-Consider this module alpha quality.
-
=head2 Basic stuff
To use this new functionality, you have to use C<fun> instead of C<sub> -
The effect of C<fun foo($bar, $baz) {> is as if you'd written
C<sub foo { my ($bar, $baz) = @_; >, i.e. the parameter list is simply
-copied into L<my|perlfunc/my> and initialized from L<@_|perlvar/"@_">.
+copied into L<my|perlfunc/my-EXPR> and initialized from L<@_|perlvar/"@_">.
In addition you can use C<method>, which understands the same syntax as C<fun>
but automatically creates a C<$self> variable for you. So by writing
The first line creates two keywords, C<proc> and C<meth> (for defining
functions and methods, respectively). The last two lines only create one
keyword. Generally the hash keys (keywords) can be any identifiers you want
-while the values (types) have to be either C<'function'>, C<'method'>,
-C<'classmethod'>, or a hash reference (see below). The main difference between
+while the values (types) have to be either a hash reference (see below) or
+C<'function'>, C<'method'>, C<'classmethod'>, C<'function_strict'>,
+C<'method_strict'>, or C<'classmethod_strict'>. The main difference between
C<'function'> and C<'method'> is that C<'method'>s automatically
L<shift|perlfunc/shift> their first argument into C<$self> (C<'classmethod'>s
are similar but shift into C<$class>).
=pod
+ use Function::Parameters ':strict';
+ # is equivalent to #
+ use Function::Parameters { fun => 'function_strict', method => 'method_strict' };
+
+=pod
+
The following shortcuts are deprecated and may be removed from a future version
of this module:
# is equivalent to #
use Function::Parameters { 'foo' => 'function', 'bar' => 'method' };
-That is, if you want to pass arguments to L<Function::Parameters>, use a
-hashref, not a list of strings.
+That is, if you want to create custom keywords with L<Function::Parameters>,
+use a hashref, not a list of strings.
-You can customize the properties of the generated keywords even more by passing
+You can tune the properties of the generated keywords even more by passing
a hashref instead of a string. This hash can have the following keys:
=over
this keyword will automatically L<shift|perlfunc/shift> its first argument into
a local variable whose name is specified here.
+=item C<invocant>
+
+Valid values: booleans. This lets users of this keyword specify an explicit
+invocant, that is, the first parameter may be followed by a C<:> (colon)
+instead of a comma and will by initialized by shifting the first element off
+C<@_>.
+
+You can combine C<shift> and C<invocant>, in which case the variable named in
+C<shift> serves as a default shift target for functions that don't specify an
+explicit invocant.
+
=item C<attributes>, C<attrs>
Valid values: strings that are valid source code for attributes. Any value
...
}
-except that none of the parameters are in scope in the expressions that specify
-default values. Thus:
+You can even refer to previous parameters in the same parameter list:
- my $var = "outer";
+ print fun ($x, $y = $x + 1) { "$x and $y" }->(9); # "9 and 10"
- fun foo($var, $wat = $var) {
- # $wat will default to "outer", not to what was passed
- # as the first argument!
- ...
- }
+This also works with the implicit first parameter of methods:
-This may change in a future version of this module.
+ method scale($factor = $self->default_factor) {
+ $self->{amount} *= $factor;
+ }
=item C<check_argument_count>
(These are all default values so C<'function'> is also equivalent to C<{}>.)
+C<'function_strict'> is like C<'function'> but with
+C<< check_argument_count => 1 >>.
+
C<'method'> is equivalent to:
{
check_argument_count => 0,
attributes => ':method',
shift => '$self',
+ invocant => 1,
}
+C<'method_strict'> is like C<'method'> but with
+C<< check_argument_count => 1 >>.
+
C<'classmethod'> is equivalent to:
{
check_argument_count => 0,
attributes => ':method',
shift => '$class',
+ invocant => 1,
}
+C<'classmethod_strict'> is like C<'classmethod'> but with
+C<< check_argument_count => 1 >>.
+
=head2 Syntax and generated code
Normally, Perl subroutines are not in scope in their own body, meaning the
body. Thus C<fun foo($x) :($) { $x }> really turns into
C<sub foo ($) { sub foo ($); my ($x) = @_; $x }>.
-If you need L<subroutine attributes|perlsub/"Subroutine Attributes">, you can
+If you need L<subroutine attributes|perlsub/Subroutine-Attributes>, you can
put them after the parameter list with their usual syntax.
Syntactically, these new parameter lists live in the spot normally occupied
As an example, the following declaration uses every available feature
(subroutine name, parameter list, default arguments, prototype, default
-attributes, attributes, argument count checks, and implicit C<$self>):
+attributes, attributes, argument count checks, and implicit C<$self> overriden
+by an explicit invocant declaration):
- method foo($x, $y, $z = sqrt 5) :($$$;$) :lvalue :Banana(2 + 2) {
+ method foo($this: $x, $y, $z = sqrt 5)
+ :($$$;$)
+ :lvalue
+ :Banana(2 + 2)
+ {
...
}
sub foo ($$$;$) :method :lvalue :Banana(2 + 2) {
sub foo ($$$;$);
- Carp::croak "Not enough arguments for method foo" if @_ < 2;
+ Carp::croak "Not enough arguments for method foo" if @_ < 3;
Carp::croak "Too many arguments for method foo" if @_ > 4;
- my $self = shift;
+ my $this = shift;
my ($x, $y, $z) = @_;
$z = sqrt 5 if @_ < 3;
...
Another example:
- my $coderef = fun ($p, $q) :(;$$)
+ my $coderef = fun ($p, $q)
+ :(;$$)
:lvalue
:Gazebo((>:O)) {
...
# vvv only if check_argument_count is enabled vvv
Carp::croak "Not enough arguments for fun (anon)" if @_ < 2;
Carp::croak "Too many arguments for fun (anon)" if @_ > 2;
+ # ^^^ ^^^
my ($p, $q) = @_;
...
};
If you want to wrap L<Function::Parameters>, you just have to call its
C<import> method. It always applies to the file that is currently being parsed
-and its effects are lexical (i.e. it works like L<warnings> or L<strict>):
+and its effects are L<lexical|perlpragma> (i.e. it works like L<warnings> or
+L<strict>).
package Some::Wrapper;
use Function::Parameters ();