Kavorka - function signatures with the lure of the animal
use Kavorka; fun maxnum (Num @numbers) { my $max = shift @numbers; for (@numbers) { $max = $_ if $max < $_; } return $max; } my $biggest = maxnum(42, 3.14159, 666);
Kavorka is still at a very early stage of development; there are likely to be many bugs that still need to be shaken out. Certain syntax features are a little odd and may need to be changed in incompatible ways.
Kavorka provides fun and method keywords for declaring functions and methods. It uses Perl 5.14's keyword API, so should work more reliably than source filters or Devel::Declare-based modules.
fun
method
The syntax provided by Kavorka is largely inspired by Perl 6, though it has also been greatly influenced by Method::Signatures and Function::Parameters.
The anatomy of a function:
The keyword introducing the function.
The function name (optional).
The signature (optional).
The prototype (optional).
The attribute list (optional).
The function body.
Example:
# (1) (2) (3) (4) (5) (6) fun foobar ($foo, $bar) :($$) :cached { return $foo + $bar } # (1) (6) my $f = fun { return $_[0] + $_[1] };
By default this module exports the keywords fun and method. These keywords are respectively implemented by Kavorka::Sub::Fun and Kavorka::Sub::Method. Other keywords may be imported upon request: after, around, before, classmethod and objectmethod.
after
around
before
classmethod
objectmethod
The module implementing the keyword may alter various aspects of the keyword's behaviour. For example, fun ensures that the function's name and prototype is declared at compile time; method shifts the invocant off @_; and before, after and around pass the body coderef to your OO framework's method modifier installation function.
@_
See the implementing modules' documentation for further details.
If present, it specifies the name of the function being defined. As with sub, if a name is present, by default the whole declaration is syntactically a statement and its effects are performed at compile time (i.e. at runtime you can call functions whose definitions only occur later in the file). If no name is present, the declaration is an expression that evaluates to a reference to the function in question.
sub
The signature consists of a list of parameters for the function.
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.
$self
${^NEXT}
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 6 signatures, Function::Parameters, and Method::Signatures.
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).
$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.
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 );
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 %_.
%_
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.
$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 ) { ... }
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!
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)
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 may be specified for each parameter in the signature:
fun foo ( Int $x, HTTP::Tiny $y ) { ... }
Type constraints are parsed as per dwim_type from Type::Utils, which should mostly do what you mean.
dwim_type
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:
use Types::Standard qw( LaxNum StrictNum ); fun foo ( ($ENV{AUTOMATED_TESTING} ? StrictNum : LaxNum) $x ) { ...; }
This feature is shared with Function::Parameters.
Value constraints can be used to further constrain values. Value constraints are specified using the where keyword followed by a block.
where
fun foo ( Int $even where { $_ % 2 == 0 } )
Multiple where blocks may be provided:
fun foo ( Int $even where { $_ % 2 == 0 } where { $_ > 0 } )
The non-block form of where supported by Method::Signatures is not supported by Kavorka, but can be emulated using match::simple:
# Method::Signatures allows this (performing smart match): # method foo ( Int $x where $y ) { ... } # For Kavorka, try this: # method foo ( Int $x where { match($_, $y) } ) { ... }
Defaults may be provided using an equals sign:
fun foo ( $greeting = "Hello world" ) { ... }
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.
when
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) ) { ... }
Traits may be added to each parameter using the is keyword:
is
fun foo ( $greeting is polite = "Hello world" ) { ... } fun bar ( $baz is quux is xyzzy ) { ... }
The keyword does is also available which acts as an alias for is.
does
You can use pretty much any word you like as a trait; Kavorka doesn't check that they're "valid" or anything. Choosing random words of course won't do anything, but the traits are available through the introspection API.
The traits Kavorka understands natively are:
alias - makes your lexical variable into an alias for an item within the @_ array.
alias
fun increment (Int $i) { ++$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.
coerce - see "Type coercion" below.
coerce
locked - 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!
locked
This trait has special support for the Dict type constraint from Types::Standard, including optional keys in the list of allowed keys.
Dict
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 - yes, the ? and ! syntax is just a shortcut for a trait.
optional
?
!
fun foo ($x is optional) { ... } # These two declarations fun foo ($x?) { ... } # are equivalent.
ro - makes the parameter a (shallow) read-only variable.
ro
fun foo ($x is ro) { $x++ } foo(42); # dies
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.
rw
is rw
slurpy - the slurpy prefix to the type constraint is just a shortcut for a trait.
slurpy
fun foo ( ArrayRef $bar is slurpy ) { ... } # These two declarations fun foo ( slurpy ArrayRef $bar ) { ... } # are equivalant
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, @lines ) { $file->spew(@lines); }
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
See also http://en.wikipedia.org/wiki/The_Yada_Yada.
Like with the sub keyword, a prototype may be provided for functions. Method dispatch ignores this, so it's only likely to be useful for fun, and even then, rarely.
Like Function::Parameters, Kavorka uses :(...) to indicate a prototype. This avoids ambiguity between signatures, prototypes and attributes.
:(...)
Attributes are parsed as per "Subroutine Attributes" in perlsub.
For anonymous functions, some attributes (e.g. :lvalue) may be applied too late to take effect. Attributes should mostly work for named functions though.
:lvalue
This is more or less what you'd expect from the function body you'd write with sub, however the lexical variables for parameters are pre-declared and pre-populated, and invocants have been shifted off @_.
Kavorka supports multi methods and multi subs:
multi method process (ArrayRef $x) { say "here" } multi method process (HashRef $x) { say "there" } __PACKAGE__->process( [] ); # here __PACKAGE__->process( {} ); # there
This feature is shared with Perl 6 signatures, though Kavorka does not support some of Perl 6's more advanced features such as multi method prototypes. (Though method modifiers should more or less work with multi methods!) Kavorka includes both type constraints and value constraints in the dispatch decision, while Perl 6 only uses type constraints.
The current implementation is not especially efficient. For example, type constraints are checked when deciding which multi method candidate to dispatch to, and then re-checked once the dispatch has been done. However, there are opportunities for future versions of Kavorka to optimize some aspects of the multi method implementation.
The word after multi (i.e. method in the above example) can be any Kavorka keyword that has been set up in the current lexical scope, provided the implementation class provides a non-undef invocation_style method (see Kavorka::Sub).
multi
invocation_style
If the invocation_style is "fun" (like Kavorka::Sub::Fun), then the signature of each candidate function in package is checked in the order in which they were defined, and the first matching candidate is dispatched to.
If the invocation_style is "method" (like Kavorka::Sub::Method), then if no successful candidate is found in the current class, candidates in superclasses are also considered.
It is possible to define alternative "long names" for the candidates of a multi method or multi sub using the :long attribute:
:long
multi fun process (ArrayRef $x) :long(process_array) { say "here"; } multi fun process (HashRef $x) :long(process_hash) { say "there"; } process($a); # multi dispatch process_array($b); # single dispatch process_hash($c); # single dispatch
(Actually, :long isn't a real attribute; we just borrow the syntax. If you try to use attributes' introspection stuff, you won't find it.)
Future versions of Kavorka might skip some type constraint checks when a candidate is called directly by its long name.
Prototypes, subroutine attributes, etc declared on the multi subs will appear on the "long name" subs, but not the multi sub.
The coderef for any sub created by Kavorka can be passed to the Kavorka->info method. This returns a blessed object that does the Kavorka::Sub role.
Kavorka->info
fun foo (:$x, :$y) { } my $info = Kavorka->info(\&foo); my $function_name = $info->qualified_name; my @named_params = $info->signature->named_params; say $named_params[0]->named_names->[0]; # says 'x'
See Kavorka::Sub, Kavorka::Signature and Kavorka::Signature::Parameter for further details.
If you're using Moose, consider using MooseX::KavorkaInfo to expose Kavorka method signatures via the meta object protocol.
-default
Exports fun and method.
-modifiers
Exports before, after, and around.
-all
Exports fun, method, before, after, around, classmethod, objectmethod, and multi.
For example:
# Everything except objectmethod... use Kavorka qw( -default -modifiers classmethod );
You can rename imported functions (see Exporter::Tiny):
use Kavorka method => { -as => 'meth' };
You can provide alternative implementations:
# use My::Sub::Method instead of Kavorka::Sub::Method use Kavorka method => { implementation => 'My::Sub::Method' };
As noted above, subroutine attributes don't work for anonymous functions.
If importing Kavorka's method modifiers into Moo/Mouse/Moose classes, pay attention to load order:
use Moose; use Kavorka -all; # ok
If you do it this way, Moose's before, after, and around keywords will stomp on top of Kavorka's...
use Kavorka -all; use Moose; # STOMP, STOMP, STOMP! :-(
Please report any bugs to http://rt.cpan.org/Dist/Display.html?Queue=Kavorka.
http://perlcabal.org/syn/S06.html, Function::Parameters, Method::Signatures.
Kavorka::Sub, Kavorka::Signature, Kavorka::Signature::Parameter.
http://en.wikipedia.org/wiki/The_Conversion_(Seinfeld).
Toby Inkster <tobyink@cpan.org>.
This software is copyright (c) 2013 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.
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.
To install Kavorka, copy and paste the appropriate command in to your terminal.
cpanm
cpanm Kavorka
CPAN shell
perl -MCPAN -e shell install Kavorka
For more information on module installation, please visit the detailed CPAN module installation guide.