X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FFunction%2FParameters.pm;h=8f58d1520df5687baa164303e3fd15bcd680bbe3;hb=e158cf8f49978f42fb2380d3f84c5df963a39a3f;hp=b0d2b2b566880cc0ce0ecaaea952a6777ec8cf9c;hpb=698e861c059c83b38a1678b02a32ff40386cac58;p=p5sagit%2FFunction-Parameters.git diff --git a/lib/Function/Parameters.pm b/lib/Function/Parameters.pm index b0d2b2b..8f58d15 100644 --- a/lib/Function/Parameters.pm +++ b/lib/Function/Parameters.pm @@ -9,7 +9,7 @@ use Carp qw(confess); use XSLoader; BEGIN { - our $VERSION = '0.06'; + our $VERSION = '0.10_01'; XSLoader::load; } @@ -32,33 +32,51 @@ my %type_map = ( 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; @@ -97,6 +115,8 @@ sub import { : 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]}"; @@ -113,6 +133,8 @@ sub import { ; $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}; @@ -154,7 +176,9 @@ Function::Parameters - subroutine definitions with parameter lists } # function with prototype - fun mymap($fun, @args) :(&@) { + fun mymap($fun, @args) + :(&@) + { my @res; for (@args) { push @res, $fun->($_); @@ -168,12 +192,17 @@ Function::Parameters - subroutine definitions with parameter lists 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; @@ -183,6 +212,22 @@ Function::Parameters - subroutine definitions with parameter lists =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', @@ -199,11 +244,6 @@ Function::Parameters - subroutine definitions with parameter lists This module lets you use parameter lists in your subroutines. Thanks to L it works without source filters. -WARNING: This is my first attempt at writing L 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 instead of C - @@ -214,7 +254,7 @@ list consists of comma-separated variables. The effect of C is as if you'd written C, i.e. the parameter list is simply -copied into L and initialized from L<@_|perlvar/"@_">. +copied into L and initialized from L<@_|perlvar/"@_">. In addition you can use C, which understands the same syntax as C but automatically creates a C<$self> variable for you. So by writing @@ -242,8 +282,9 @@ Or more concretely: The first line creates two keywords, C and C (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 their first argument into C<$self> (C<'classmethod'>s are similar but shift into C<$class>). @@ -258,6 +299,12 @@ The following shortcuts are available: =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: @@ -275,10 +322,10 @@ of this module: # is equivalent to # use Function::Parameters { 'foo' => 'function', 'bar' => 'method' }; -That is, if you want to pass arguments to L, use a -hashref, not a list of strings. +That is, if you want to create custom keywords with L, +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 @@ -296,6 +343,17 @@ Valid values: strings that look like a scalar variable. Any function created by this keyword will automatically L its first argument into a local variable whose name is specified here. +=item C + +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 and C, in which case the variable named in +C serves as a default shift target for functions that don't specify an +explicit invocant. + =item C, C Valid values: strings that are valid source code for attributes. Any value @@ -336,18 +394,15 @@ turns into ... } -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 @@ -382,6 +437,9 @@ Plain C<'function'> is equivalent to: (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: { @@ -390,8 +448,12 @@ 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: { @@ -400,8 +462,12 @@ 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 @@ -415,7 +481,7 @@ so the parser knows the name (and possibly prototype) while it processes the body. Thus C really turns into C. -If you need L, you can +If you need L, you can put them after the parameter list with their usual syntax. Syntactically, these new parameter lists live in the spot normally occupied @@ -426,9 +492,14 @@ with C<(>). 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) + { ... } @@ -436,9 +507,9 @@ And here's what it turns into: 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; ... @@ -446,7 +517,8 @@ And here's what it turns into: Another example: - my $coderef = fun ($p, $q) :(;$$) + my $coderef = fun ($p, $q) + :(;$$) :lvalue :Gazebo((>:O)) { ... @@ -458,6 +530,7 @@ And the generated code: # 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) = @_; ... }; @@ -466,7 +539,8 @@ And the generated code: If you want to wrap L, you just have to call its C method. It always applies to the file that is currently being parsed -and its effects are lexical (i.e. it works like L or L): +and its effects are L (i.e. it works like L or +L). package Some::Wrapper; use Function::Parameters ();