NAME
Kavorka::Manual::Signatures - experience the lure of the animal
DESCRIPTION
The signature consists of a list of parameters for the function.
Kavorka signatures are a superset of the sub signature syntax introduced in Perl 5.20 (well, 5.19.9 to be exact). See perlsub.
Each parameter is a variable name which will be available within the body of the function. Variable names are assumed to be lexicals unless they look like punctuation variables or escape-character global variables, in which case they'll be implicitly localized within the function.
Parameters are separated with commas, however if one of the commas is replaced by a colon, all parameters to the left are assumed to be invocants and are shifted off @_
. If no invocants are explicitly listed as part of the signature, the module implementing the keyword may assume a default invocant - for example, method
assumes an invocant called $self
while around
assumes two invocants called $next
and $self
.
Positional parameters
Parameters which are not explicitly named, slurpy or invocants, are positional. For example:
method foo ( $x, $y ) { ... }
Is roughly equivalent to:
sub foo {
my $self = shift;
die "Expected two parameters" unless @_ == 2;
my ($x, $y) = @_;
...
}
This feature is shared with Perl 5.20 signatures, Perl 6 signatures, Function::Parameters, and Method::Signatures.
Invocants
Invocants are a type of positional parameter, which instead of being copied from the @_
array are shifted off it.
Invocants are always required, and cannot have defaults. Some keywords (such as method
and classmethod
) provide a standard invocant for you (respectively $self
and $class
).
You may specify invocants in the signature manually, in which case the default provided by the keyword is ignored.
# The invocant is called $me instead of $self
method ($me: $x, $y?) { ... }
This feature is shared with Perl 6 signatures, Function::Parameters, and Method::Signatures. Unique to Kavorka is the ability to specify multiple invocants.
Named parameters
Parameters can be named by preceding them with a colon:
method foo ( :$x, :$y ) { ... }
The method would be called like this:
$object->foo( x => 1, y => 2 );
Or this:
$object->foo({ x => 1, y => 2 });
This feature is shared with Perl 6 signatures, Function::Parameters, and Method::Signatures.
Positional parameters (if any) must precede named parameters.
If you have any named parameters, they will also be made available in the magic global hash %_
. If you pass a hashref (rather than a hash) of named parameters, then %_
will be an alias for the referenced hash.
Long name parameters
Named parameters can be given a different name "inside" and "outside" the function:
fun bar ( :public_house($pub) ) { ... }
The function would be called like this:
bar( public_house => "Rose & Crown" );
... But within the function, the variable would be named $pub
.
This feature is shared with Perl 6 signatures.
Long named parameters will be available in %_
under their "outside" name, not their "inside" name.
A function can have multiple long names:
fun xxx ( :foo(:bar(:baz($x))) ) { ... }
This unwieldy syntax is borrowed from Perl 6 signatures.
Kavorka provides an experimental shortcut - you may omit the parentheses:
fun xxx ( :foo :bar :baz $x ) { ... }
Global variables
The variables established by Kavorka are normally plain old lexicals (my
variables). However, you can instead make them into localised package variables (our
variables):
fun xxx ( Int our $x ) { ... }
Variables containing "::", the special globals $_
, @_
, and %_
, and variables named like ${^HELLO}
are automatically localized.
(The other special punctuation variables listed in perlvar are not supported.)
Optional and required parameters
A trailing exclamation mark makes an attribute required. A trailing question mark makes an attribute optional.
This feature is shared with Perl 6 signatures and Method::Signatures.
In the absence of explicit indicators, positional parameters will be required unless a default is provided for them, and named parameters will be optional.
You can not use named parameters and optional positional parameters in the same signature.
For long named parameters, the trailing indicator should appear after the close parentheses:
fun xxx ( :foo($x)! ) { ... }
fun xxx ( :foo($x!) ) { ... } # NO!
Slurpy parameters
The final parameter in the signature may be an array or hash, which will consume all remaining arguments:
fun foo ( $x, $y, %z ) { ... }
foo(1..4); # %z is (3 => 4)
This feature is shared with Perl 6 signatures, Function::Parameters, and Method::Signatures.
A slurpy array may not be used if the signature contains any named parameters.
Unique to Kavorka is the ability to specify slurpy arrayrefs or hashrefs.
fun foo ( $x, $y, slurpy HashRef $z ) { ... }
foo(1..4); # $z is { 3 => 4 }
For slurpy references you should specify a type constraint (see "Type Constraints") so that Kavorka can create the correct type of reference.
The variables @_
and %_
may be used as slurpy parameters, but only if their use as a parameter does not interfere with their usual meaning.
# ok
fun foo ( @_ ) {
...;
}
# disallowed because the @_ array would usually include $x
fun bar ( $x, @_ ) {
...;
}
# ok because the invocant $x would usually be shifted off @_
fun baz ( $x: @_ ) {
...;
}
Type constraints
Type constraints may be specified for each parameter in the signature:
fun foo ( Int $x, HTTP::Tiny $y ) { ... }
This feature is shared with Perl 6 signatures, Function::Parameters, and Method::Signatures.
Type constraints are parsed as per dwim_type
from Type::Utils, which should mostly do what you mean.
Type constraints for slurpy hashes and arrays are applied to each value in the hash or each item in the array. Type constraints for slurpy references are instead applied to the reference as a whole. Therefore the following are roughly equivalent:
fun foo ( Str %z ) { my $z = \%z; ... }
fun foo ( slurpy HashRef[Str] $z ) { ... }
Type constraints may be surrounded with parentheses, in which case, instead of parsing them with dwim_type
, they'll be evaluated (at compile time) as an expression which is expected to return a blessed Type::Tiny object, or any other value that Types::TypeTiny can coerce to a Type::Tiny object.
use Types::Standard qw( LaxNum StrictNum );
fun foo ( ($ENV{AUTOMATED_TESTING} ? StrictNum : LaxNum) $x ) {
...;
}
This feature is shared with Function::Parameters.
Value constraints
Value constraints can be used to further constrain values. Value constraints are specified using the where
keyword followed by a block.
fun foo ( Int $even where { $_ % 2 == 0 } )
Multiple where
blocks may be provided:
fun foo ( Int $even where { $_ % 2 == 0 } where { $_ > 0 } )
This feature is shared with Perl 6 signatures and Method::Signatures.
A smartmatch-like shortcut is supported:
# This is a shortcut:
#
method foo ( Int $x where TERM ) {
...
}
# For this:
#
use match::simple ();
method foo ( Int $x where { match::simple::match($_, TERM) } ) {
...
}
However, the use of any lexical variables within the term is currently broken.
Defaults
Defaults may be provided using an equals sign:
fun foo ( $greeting = "Hello world" ) {
...
}
This feature is shared with Perl 5.20 signatures, Perl 6 signatures, Function::Parameters, and Method::Signatures.
Kavorka will use the default if the argument is not given when the function is invoked. If an explicit undef is passed to the function when it is called, this is accepted as the value for the parameter, and the default is not used.
If instead you want the default to take effect when an explicit undef is passed to the function, use //=
:
fun foo ( $greeting //= "Hello world" ) {
...
}
This feature is shared with Method::Signatures. Kavorka doesn't support Method::Signatures' when
keyword.
||=
is also supported for setting defaults. It kicks in when any false value (undef, zero, the empty string, or objects overloading boolification to return false) is passed to the function, though this is rarely of much use.
Slurpy parameters may take defaults:
fun foo ( @bar = (1, 2, 3) ) { ... }
For slurpy references, the syntax is a little unintuitive:
fun foo ( slurpy ArrayRef $bar = (1, 2, 3) ) { ... }
Caveat: unlike Perl 5.20 signatures, the following does not work:
fun foo ($x, $y = $x) { ... }
The lexical variable $x
has scope in the body of the sub, but not within the signature itself, so it cannot be used as the default value for $y
. This can be worked around by making $x
a package variable:
fun foo (our $x, $y = our $x) { ... }
Or simply set the default manually in the body of the function.
fun foo ($x, $y?) {
$y = $x if @_ == 1;
...;
}
You hadn't forgotten how to do that already, had you?
Traits
Traits may be added to each parameter using the is
keyword:
fun foo ( $greeting is polite = "Hello world" ) { ... }
fun bar ( $baz is quux is xyzzy ) { ... }
This feature is shared with Perl 6 signatures and Method::Signatures.
The keywords does
and but
are also available which are simply aliases for is
.
Many traits are recognized by Kavorka natively. If you use an unrecognized trait, such as foo
, Kavorka will attempt to load it as a Moo::Role (see Kavorka::Manual::ExtendingKavorka).
The traits Kavorka understands natively are:
alias
This makes your lexical variable into an alias for an item within the @_
array.
fun increment (Int $i is alias) { ++$i }
my $count = 0;
increment($count);
increment($count);
increment($count);
say $count; # says 3
But please don't use this for parameters with coercions!
This feature is shared with Method::Signatures.
assumed
Switches off the type constraint check, though the original type constraint is still visible through the introspection API.
fun increment (Int $i is alias but assumed) { ++$i }
my $count = 1.1;
increment($count); # called incorrectly but will not die
This can be used to switch off slow type constraint checks while keeping the original type constraint in the signature to express your intent.
This trait cannot be used in multi subs, where the type check is instrumental in deciding which candidate sub to dispatch to. It can be used in conjunction with the coerce
trait, in which case the type constraint will be checked to determine whether coercion is necessary, but no type check will be performed on the result of the coercion.
coerce
See "Type coercion" below.
copy
This is the opposite of alias, and is the default. It makes the lexical variable a (very shallow) copy of the argument from @_
. That is, it's equivalent to doing:
my $param = $_[$i];
# whereas the `alias` trait does this...
Data::Alias::alias(my $param = $_[$i]);
locked
This locks hash(ref) keys - see Hash::Util. For references this trait has the unfortunate side-effect of leaving the hashref locked outside the function too!
This trait has special support for the Dict
type constraint from Types::Standard, including optional keys in the list of allowed keys, unless the type constraint includes a slurpy
component.
fun foo (HashRef $x is locked) {
$x->{foo} = 1;
}
my $var1 = { foo => 42 };
foo($var1);
say $var1->{foo}; # says 1
my $var2 = { bar => 42 };
foo($var2); # dies
optional
The ?
and !
syntax is just a shortcut for applying or not applying this trait.
fun foo ($x is optional) { ... } # These two declarations
fun foo ($x?) { ... } # are equivalent.
ref_alias
The ref_alias
trait allows you to pass an arrayref, hashref or scalarref to the function, and have it available as a plain array, hash or scalar. For example:
fun get_key_from_hash(%h is ref_alias, $key) {
return $h{$key};
}
my %data = ( foo => 42 );
get_key_from_hash(\%data, 'foo'); # returns 42
It's the only useful way to define non-slurpy hash or array parameters within a signature.
A backslash before the variable name can be used as a shortcut for this trait:
fun get_key_from_hash(\%h, $key) {
return $h{$key};
}
This feature is shared with Method::Signatures.
ro
This makes the parameter a (shallow) read-only variable.
fun foo ($x is ro) { $x++ }
foo(42); # dies
This feature is shared with Perl 6 signatures.
rw
This is the default, so is a no-op, but if you have a mixture of read-only and read-write variables, it may aid clarity to explicitly add is rw
to the read-write ones.
slurpy
The slurpy prefix to the type constraint is just a shortcut for a trait.
fun foo ( ArrayRef $bar is slurpy ) { ... } # These two declarations
fun foo ( slurpy ArrayRef $bar ) { ... } # are equivalant
Type coercion
Coercion can be enabled for a parameter using the coerce
trait.
use Types::Path::Tiny qw(AbsPath);
method print_to_file ( AbsFile $file does coerce, Str @lines ) {
$file->spew(@lines);
}
This feature is shared with Method::Signatures.
The Yada Yada
Normally passing additional parameters to a function declared with a signature will throw an exception:
fun foo ($x) {
return $x;
}
foo(1, 2); # error - too many arguments
Adding the yada yada operator to the end of the signature allows the function to accept extra trailing parameters:
fun foo ($x, ...) {
return $x;
}
foo(1, 2); # ok
This feature is shared with Method::Signatures.
See also http://en.wikipedia.org/wiki/The_Yada_Yada.
Return types
After a Unicode rightwards arrow character (→
) or the ASCII equivalent (-->
), you may list return type constraints.
For a function which takes three values and returns an integer:
fun foo ($a, $b, $c → Int) { ... }
It is possible to include a return type for functions which take no parameters:
fun foo (→ Int) { ... }
For a function which returns a list in list context, you can use the list
and scalar
traits to specify return types for each context.
fun foo ($a → Int is scalar, ArrayRef[Int] is list) { ... }
Note that the list of returned values is validated as if it were an arrayref (or a hashref if that seems more appropriate).
If no type constraint is provided for list context, then the type constraint is assumed to be an arrayref of whatever type constraint was given for scalar context.
Return types can be coerced:
fun foo ($a → Int does coerce) { ... }
Return types are implemented using Return::Type which adds a wrapper around your function. Although this wrapper should be invisible to caller
(thanks to Scope::Upper), it does add some overhead to your function calls, so return types are a feature to use conservatively.
If a return type has the assumed
trait, it will not be checked at run time; we just assume the function is doing its job properly and returning an appropriate value. This avoids the overhead of checking return types at run time, but still includes the return type constraint in the introspection API.
fun plus_one (Int $x → Int but assumed)
{
# No need to check return type.
# If $x is an Int, then $x+1 must be too!
return $x + 1;
}
BUGS
Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=Kavorka.
SEE ALSO
AUTHOR
Toby Inkster <tobyink@cpan.org>.
COPYRIGHT AND LICENCE
This software is copyright (c) 2013-2014, 2017 by Toby Inkster.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.
DISCLAIMER OF WARRANTIES
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.