Functions
Functions and functional programming in Perl 6
Routines are one of the means Perl 6 has to reuse code. They come in several forms, most notably methods, which belong in classes and roles and are associated with an object; and functions (also called subroutines or subs, for short), which can be called independently of objects.
Subroutines default to lexical (my
) scoping, and calls to them are generally resolved at compile time.
Subroutines can have a signature, also called parameter list, which specifies which, if any, arguments the signature expects. It can specify (or leave open) both the number and types of arguments, and the return value.
Introspection on subroutines is provided via Routine
.
Defining/Creating/Using functions
Subroutines
The basic way to create a subroutine is to use the sub
declarator followed by an optional identifier:
sub my-funcmy-func;
The sub declarator returns a value of type Sub that can be stored in any container:
my = subc; # OUTPUT: «Look ma, no name!»my Any = sub(); # OUTPUT: «Still nameless...»my Code \a = sub ;a.(); # OUTPUT: «raw containers don't implement postcircumfix:<( )>»
The declarator sub
will declare a new name in the current scope at compile time. As such, any indirection has to be resolved at compile time:
constant aname = 'foo';sub ::(aname) ;foo;
This will become more useful once macros are added to Perl 6.
To have the subroutine take arguments, a signature goes between the subroutine's name and its body, in parentheses:
sub exclaim ($phrase) { say $phrase ~ "!!!!" } exclaim "Howdy, World";
By default, subroutines are lexically scoped. That is, sub foo {...}
is the same as my sub foo {...}
and is only defined within the current scope.
sub escape()say escape 'foo#bar?'; # OUTPUT: «foo\#bar\?»# Back to original escape functionsay escape 'foo#bar?'; # OUTPUT: «foo\#bar\?»
Subroutines don't have to be named. If unnamed, they're called anonymous subroutines.
say sub (, ) (3, 4) # OUTPUT: «25»
But in this case, it's often desirable to use the more succinct block syntax. Subroutines and blocks can be called in place, as in the example above.
say -> , (3, 4) # OUTPUT: «25»
Or even
say (3, 4) # OUTPUT: «25»
Blocks and lambdas
Whenever you see something like { $_ + 42 }
, -> $a, $b { $a ** $b }
, or { $^text.indent($:spaces) }
, that's Block syntax; the ->
is considered also part of the block. Statements such as if
, for
, while
are followed by these kind of blocks.
for 1, 2, 3, 4 -> ,# OUTPUT: «1234»
They can also be used on their own as anonymous blocks of code.
say (3, 4) # OUTPUT: «25»
Please note that this implies that, despite the fact that statements such as if
do not define a topic variable, they actually can:
my = 33;if ** 33 -># OUTPUT: «129110040087761027839616029934664535539337183380513 is not null»
For block syntax details, see the documentation for the Block type.
Signatures
The parameters that a function accepts are described in its signature.
sub format(Str $s) { ... } -> $a, $b { ... }
Details about the syntax and use of signatures can be found in the documentation on the Signature
class.
Automatic signatures
If no signature is provided but either of the two automatic variables @_
or %_
are used in the function body, a signature with *@_
or *%_
will be generated. Both automatic variables can be used at the same time.
sub s ;say .signature # OUTPUT: «(*@_, *%_)»
Arguments
Arguments are supplied as a comma separated list. To disambiguate nested calls, use parentheses:
sub f(); # call the function reference c with empty parameter listsub g();say(g(42), 45); # pass only 42 to g()
When calling a function, positional arguments should be supplied in the same order as the function's signature. Named arguments may be supplied in any order, but it's considered good form to place named arguments after positional arguments. Inside the argument list of a function call, some special syntax is supported:
sub f(|c);f :named(35); # A named argument (in "adverb" form)f named => 35; # Also a named argumentf :35named; # A named argument using abbreviated adverb formf 'named' => 35; # Not a named argument, a Pair in a positional argumentmy \c = <a b c>.Capture;f |c; # Merge the contents of Capture $c as if they were supplied
Arguments passed to a function are conceptually first collected in a Capture
container. Details about the syntax and use of these containers can be found in the documentation on the Capture
class.
When using named arguments, note that normal List "pair-chaining" allows one to skip commas between named arguments.
sub f(|c);f :dest</tmp/foo> :src</tmp/bar> :lines(512);f :32x :50y :110z; # This flavor of "adverb" works, toof :a:b:c; # The spaces are also optional.
Return values
Any Block
or Routine
will provide the value of its last expression as a return value to the caller. If either return or return-rw is called, then its parameter, if any, will become the return value. The default return value is Nil.
sub a ;sub b ;sub c ;b; # OUTPUT: «42»say c; # OUTPUT: «Nil»
Multiple return values are returned as a list or by creating a Capture. Destructuring can be used to untangle multiple return values.
sub a ;put a.perl;# OUTPUT: «(42, "answer")»my (, ) = a;put [, ];# OUTPUT: «answer 42»sub b ;put b.perl;# OUTPUT: «\("a", "b", "c")»
Return type constraints
Perl 6 has many ways to specify a function's return type:
sub foo(--> Int) ; say .returns; # OUTPUT: «(Int)»
sub foo() returns Int ; say .returns; # OUTPUT: «(Int)»
sub foo() of Int ; say .returns; # OUTPUT: «(Int)»
my Int sub foo() ; say .returns; # OUTPUT: «(Int)»
Attempting to return values of another type will cause a compilation error.
sub foo() returns Int ; foo; # Type check fails
returns
and of
are equivalent, and both take only a Type since they are declaring a trait of the Callable. The last declaration is, in fact, a type declaration, which obviously can take only a type. In the other hand, -->
can take either undefined or definite values.
Note that Nil
and Failure
are exempt from return type constraints and can be returned from any routine, regardless of its constraint:
sub foo() returns Int ; foo; # Failure returnedsub bar() returns Int ; bar; # Nil returned
Multi-dispatch
Perl 6 allows for writing several routines with the same name but different signatures. When the routine is called by name, the runtime environment determines the proper candidate and invokes it.
Each candidate is declared with the multi
keyword. Dispatch happens depending on the number (arity), type and name of arguments. Consider the following example:
# version 1multi happy-birthday( )# version 2multi happy-birthday( , )# version 3multi happy-birthday( :, :, : = 'Mr' )# calls version 1 (arity)happy-birthday 'Larry'; # OUTPUT: «Happy Birthday Larry !»# calls version 2 (arity)happy-birthday 'Luca', 40; # OUTPUT: «Happy 40th Birthday Luca !»# calls version 3# (named arguments win against arity)happy-birthday( age => '50', name => 'John' ); # OUTPUT: «Happy 50th Birthday Mr John !»# calls version 2 (arity)happy-birthday( 'Jack', 25 ); # OUTPUT: «Happy 25th Birthday Jack !»
The first two versions of the happy-birthday
sub differs only in the arity (number of arguments), while the third version uses named arguments and is chosen only when named arguments are used, even if the arity is the same of another multi
candidate.
When two sub have the same arity, the type of the arguments drive the dispatch; when there are named arguments they drive the dispatch even when their type is the same as another candidate:
multi happy-birthday( Str , Int )multi happy-birthday( Str , Str )multi happy-birthday( Str :, Int : )happy-birthday 'Luca', 40; # OUTPUT: «Happy 40th Birthday Luca !»happy-birthday 'Luca', 'Mr'; # OUTPUT: «Happy Birthday Mr Luca !»happy-birthday age => 40, name => 'Luca'; # OUTPUT: «Happy Birthday Luca, you turned 40 !»
Named parameters participate in the dispatch even if they are not provided in the call. Therefore a multi candidate with named parameters will be given precedence.
For more information about type constraints see the documentation for the Signature class.
multi as-json(Bool )multi as-json(Real )multi as-json()say as-json( True ); # OUTPUT: «true»say as-json( 10.3 ); # OUTPUT: «10.3»say as-json( [ True, 10.3, False, 24 ] ); # OUTPUT: «[true, 10.3, false, 24]»
multi
without any specific routine type always defaults to a sub
, but you can use it on methods as well. The candidates are all the multi methods of the object:
my = Congrats.new does BirthdayCongrats;.congratulate('promotion','Cindy'); # OUTPUT: «Hooray for your promotion, Cindy».congratulate('birthday','Bob'); # OUTPUT: «Happy birthday, Bob»
Unlike sub
, if you use named parameters with multi methods, the parameters must be required parameters to behave as expected.
Please note that a non-multi sub or operator will hide multi candidates of the same name in any parent scope or child scope. The same is true for imported non-multi candidates.
proto
proto
is a way to formally declare commonalities between multi
candidates. It acts as a wrapper that can validate but not modify arguments. Consider this basic example:
proto congratulate(Str , Str , |)multi congratulate(, )multi congratulate(, , Int )congratulate('being a cool number', 'Fred'); # OKcongratulate('being a cool number', 'Fred', 42); # OK
congratulate('being a cool number', 42); # Proto match error
The proto insists that all multi congratulate
subs conform to the basic signature of two strings, optionally followed by further parameters. The |
is an un-named Capture
parameter, and allows a multi
to take additional arguments. The first two calls succeed, but the third fails (at compile time) because 42
doesn't match Str
.
say .signature # OUTPUT: «(Str $reason, Str $name, | is raw)»
You can give the proto
a function body, and place the {*}
where you want the dispatch to be done.
# attempts to notify someone -- False if unsuccessfulproto notify(Str , Str )
{*}
always dispatches to candidates with the parameters it's called with. Parameter defaults and type coercions will work but are not passed on.
proto mistake-proto(Str() , Int = 42)multi mistake-proto(, )mistake-proto(7, 42); # OUTPUT: «Int» -- not passed on
mistake-proto('test'); # fails -- not passed on
only
The only
keyword preceding sub
or method
indicates that it will be the only function with that name that inhabits a given namespace.
only sub you () ;
This will make other declarations in the same namespace, such as
sub you ( )
fail with an exception of type X::Redeclaration
. only
is the default value for all subs; in the case above, not declaring the first subroutine as only
will yield exactly the same error; however, nothing prevents future developers from declaring a proto and preceding the names with multi
. Using only
before a routine is a defensive programming feature that declares the intention of not having routines with the same name declared in the same namespace in the future.
(exit code 1)===SORRY!=== Error while compiling /tmp/only-redeclaration.p6Redeclaration of routine 'you' (did you mean to declare a multi-sub?)at /tmp/only-redeclaration.p6:3------> <BOL>⏏<EOL>
Anonymous sub cannot be declared only
. only sub {}'
will throw an error of type, surprisingly, X::Anon::Multi
.
Conventions and idioms
While the dispatch system described above provides a lot of flexibility, there are some conventions that most internal functions, and those in many modules, will follow.
Slurpy conventions
Perhaps the most important one of these conventions is the way slurpy list arguments are handled. Most of the time, functions will not automatically flatten slurpy lists. The rare exceptions are those functions that don't have a reasonable behavior on lists of lists (e.g., chrs) or where there is a conflict with an established idiom (e.g., pop being the inverse of push).
If you wish to match this look and feel, any Iterable argument must be broken out element-by-element using a **@
slurpy, with two nuances:
An Iterable inside a Scalar container doesn't count.
Lists created with a
,
at the top level only count as one Iterable.
This can be achieved by using a slurpy with a +
or +@
instead of **
:
sub grab(+)
which is shorthand for something very close to:
multi sub grab(**)multi sub grab(\a)
This results in the following behavior, which is known as the "single argument rule" and is important to understand when invoking slurpy functions:
grab(1, 2); # OUTPUT: «grab 1grab 2»grab((1, 2)); # OUTPUT: «grab 1grab 2»grab($(1, 2)); # OUTPUT: «grab 1 2»grab((1, 2), 3); # OUTPUT: «grab 1 2grab 3»
This also makes user-requested flattening feel consistent whether there is one sublist, or many:
grab(flat (1, 2), (3, 4)); # OUTPUT: «grab 1grab 2grab 3grab 4»grab(flat $(1, 2), $(3, 4)); # OUTPUT: «grab 1 2grab 3 4»grab(flat (1, 2)); # OUTPUT: «grab 1grab 2»grab(flat $(1, 2)); # OUTPUT: «grab 1grab 2»
It's worth noting that mixing binding and sigilless variables in these cases requires a bit of finesse, because there is no Scalar intermediary used during binding.
my = (1, 2); # Normal assignment, equivalent to $(1, 2)grab(); # OUTPUT: «grab 1 2»my := (1, 2); # Binding, $b links directly to a bare (1, 2)grab(); # OUTPUT: «grab 1grab 2»my \c = (1, 2); # Sigilless variables always bind, even with '='grab(c); # OUTPUT: «grab 1grab 2»
Functions are first-class objects
Functions and other code objects can be passed around as values, just like any other object.
There are several ways to get hold of a code object. You can assign it to a variable at the point of declaration:
my = sub (Numeric )# and then use it:say (6); # OUTPUT: «36»
Or you can reference an existing named function by using the &
-sigil in front of it.
sub square() ;# get hold of a reference to the function:my =
This is very useful for higher order functions, that is, functions that take other functions as input. A simple one is map, which applies a function to each input element:
sub square() ;my = map , 1..5;say join ', ', ; # OUTPUT: «1, 4, 9, 16, 25»
Infix form
To call a subroutine with 2 arguments like an infix operator, use a subroutine reference surrounded by [
and ]
.
sub plus ;say 21 [] 21;# OUTPUT: «42»
Closures
All code objects in Perl 6 are closures, which means they can reference lexical variables from an outer scope.
sub generate-sub()my = generate-sub(21);(); # OUTPUT: «42»
Here, $y
is a lexical variable inside generate-sub
, and the inner subroutine that is returned uses it. By the time that inner sub is called, generate-sub
has already exited. Yet the inner sub can still use $y
, because it closed over the variable.
Another closure example is the use of map to multiply a list of numbers:
my = 5;say join ', ', map , 1..5; # OUTPUT: «5, 10, 15, 20, 25»
Here, the block passed to map
references the variable $multiply-by
from the outer scope, making the block a closure.
Languages without closures cannot easily provide higher-order functions that are as easy to use and powerful as map
.
Routines
Routines are code objects that conform to type Routine
, most notably Sub
, Method
, Regex
and Submethod
.
They carry extra functionality in addition to what a Block
supplies: they can come as multis, you can wrap them, and exit early with return
:
my = set <if for unless while>;sub has-keyword(*)say has-keyword 'not', 'one', 'here'; # OUTPUT: «False»say has-keyword 'but', 'here', 'for'; # OUTPUT: «True»
Here, return
doesn't just leave the block inside which it was called, but the whole routine. In general, blocks are transparent to return
, they attach to the outermost routine.
Routines can be inlined and as such provide an obstacle for wrapping. Use the pragma use soft;
to prevent inlining to allow wrapping at runtime.
sub testee(Int , Str )sub wrap-to-debug()my = wrap-to-debug();# OUTPUT: «wrapping testee with arguments :(Int $i, Str $s)»say testee(10, "ten");# OUTPUT: «calling testee with \(10, "ten")returned from testee with return value "6.151190ten"6.151190ten».unwrap();say testee(10, "ten");# OUTPUT: «6.151190ten»
Defining operators
Operators are just subroutines with funny names. The funny names are composed of the category name (infix
, prefix
, postfix
, circumfix
, postcircumfix
), followed by a colon, and a list of the operator name or names (two components in the case of circumfix and postcircumfix).
This works both for adding multi candidates to existing operators and for defining new ones. In the latter case, the definition of the new subroutine automatically installs the new operator into the grammar, but only in the current lexical scope. Importing an operator via use
or import
also makes it available.
# adding a multi candidate to an existing operator:multi infix:<+>(Int , "same") ;say 21 + "same"; # OUTPUT: «42»# defining a new operatorsub postfix:<!>(Int where ) ;say 6!; # OUTPUT: «720»
The operator declaration becomes available as soon as possible, so you can recurse into a just-defined operator:
sub postfix:<!>(Int where )say 6!; # OUTPUT: «720»
Circumfix and postcircumfix operators are made of two delimiters, one opening and one closing.
sub circumfix:<START END>(*)say START 'a', 'b', 'c' END; # OUTPUT: «(start [a b c] end)»
Postcircumfixes also receive the term after which they are parsed as an argument:
sub postcircumfix:<!! !!>(, )say 42!! 1 !!; # OUTPUT: «42 -> ( 1 )»
Blocks can be assigned directly to operator names. Use a variable declarator and prefix the operator name with a &
-sigil.
my :<ieq> = -> |l ;say "abc" ieq "Abc";# OUTPUT: «True»
Precedence
Operator precedence in Perl 6 is specified relative to existing operators. The traits is tighter
, is equiv
and is looser
can be provided with an operator to indicate how the precedence of the new operator is related to other, existing ones. More than one trait can be applied.
For example, infix:<*>
has a tighter precedence than infix:<+>
, and squeezing one in between works like this:
sub infix:<!!>(, ) is tighter(:<+>)say 1 + 2 * 3 !! 4; # OUTPUT: «21»
Here, the 1 + 2 * 3 !! 4
is parsed as 1 + ((2 * 3) !! 4)
, because the precedence of the new !!
operator is between that of +
and *
.
The same effect could have been achieved with:
sub infix:<!!>(, ) is looser(:<*>)
To put a new operator on the same precedence level as an existing operator, use is equiv(&other-operator)
instead.
Associativity
When the same operator appears several times in a row, there are multiple possible interpretations. For example:
1 + 2 + 3
could be parsed as
(1 + 2) + 3 # left associative
or as
1 + (2 + 3) # right associative
For addition of real numbers, the distinction is somewhat moot, because +
is mathematically associative.
But for other operators it matters a great deal. For example, for the exponentiation/power operator, infix:<**>
:
say 2 ** (2 ** 3); # OUTPUT: «256»say (2 ** 2) ** 3; # OUTPUT: «64»
Perl 6 has the following possible associativity configurations:
A | Assoc | Meaning of $a ! $b ! $c |
---|---|---|
L | left | ($a ! $b) ! $c |
R | right | $a ! ($b ! $c) |
N | non | ILLEGAL |
C | chain | ($a ! $b) and ($b ! $c) |
X | list | infix:<!>($a; $b; $c) |
You can specify the associativity of an operator with the is assoc
trait, where left
is the default associativity.
sub infix:<§>(*) is assoc<list>say 1 § 2 § 3; # OUTPUT: «(1|2|3)»
Traits
Traits are subroutines that run at compile time and modify the behavior of a type, variable, routine, attribute, or other language object.
Examples of traits are:
is ParentClass# ^^ trait, with argument ParentClasshas is rw;# ^^^^^ trait with name 'rw'does AnotherRole# ^^^^ traithas handles <close>;# ^^^^^^^ trait
... and also is tighter
, is looser
, is equiv
and is assoc
from the previous section.
Traits are subs declared in the form trait_mod<VERB>
, where VERB
stands for the name like is
, does
or handles
. It receives the modified thing as argument, and the name as a named argument. See Sub for details.
multi sub trait_mod:<is>(Routine , :!)sub square() is doublessay square 3; # OUTPUT: «18»
See type Routine for the documentation of built-in routine traits.
Re-dispatching
There are cases in which a routine might want to call the next method from a chain. This chain could be a list of parent classes in a class hierarchy, or it could be less specific multi candidates from a multi dispatch, or it could be the inner routine from a wrap
.
Fortunately, we have a series of re-dispatching tools that help us to make it easy.
sub callsame
callsame
calls the next matching candidate with the same arguments that were used for the current candidate and returns that candidate's return value.
proto a(|)multi a(Any )multi a(Int )a 1; # OUTPUT: «Int 1Any 1Back in Int with 5»
sub callwith
callwith
calls the next candidate matching the original signature, that is, the next function that could possibly be used with the arguments provided by users and returns that candidate's return value.
proto a(|)multi a(Any )multi a(Int )a 1; # OUTPUT: «Int 1Any 2Back in Int with 5»
Here, a 1
calls the most specific Int
candidate first, and callwith
re-dispatches to the less specific Any
candidate. Note that although our parameter $x + 1
is an Int
, still we call the next candidate in the chain.
In this case, for example:
proto how-many(|)multi how-many( Associative )multi how-many( Pair )multi how-many( Hash )my = little => 'piggy';say .^name; # OUTPUT: «Pair»say .cando( \( ));# OUTPUT: «(sub how-many (Pair $a) { #`(Sub|68970512) ... } sub how-many (Associative $a) { #`(Sub|68970664) ... })»say how-many( ); # OUTPUT: «Pair little piggyThere is little piggy»
the only candidates that take the Pair
argument supplied by the user are the two functions defined first. Although a Pair
can be easily coerced to a Hash
, here is how signatures match:
say :( Pair ) ~~ :( Associative ); # OUTPUT: «True»say :( Pair ) ~~ :( Hash ); # OUTPUT: «False»
The arguments provided by us are a Pair
. It does not match a Hash
, so the corresponding function is thus not included in the list of candidates, as can be seen by the output of &how-many.cando( \( $little-piggy ));
.
sub nextsame
nextsame
calls the next matching candidate with the same arguments that were used for the current candidate and never returns.
proto a(|)multi a(Any )multi a(Int )a 1; # OUTPUT: «Int 1Any 1»
sub nextwith
nextwith
calls the next matching candidate with arguments provided by users and never returns.
proto a(|)multi a(Any )multi a(Int )a 1; # OUTPUT: «Int 1Any 2»
sub samewith
samewith
calls current candidate again with arguments provided by users and returns return value of the new instance of current candidate.
proto a(|)multi a(Int )say (a 10); # OUTPUT: «36288002»
sub nextcallee
Redispatch may be required to call a block that is not the current scope what provides nextsame
and friends with the problem to referring to the wrong scope. Use nextcallee
to capture the right candidate and call it at the desired time.
proto pick-winner(|)multi pick-winner (Int \s)multi pick-winnerwith pick-winner ^5 .pick -> \result# OUTPUT:# And the winner is...# Woot! 3 won
The Int
candidate takes the nextcallee
and then fires up a Promise
to be executed in parallel, after some timeout, and then returns. We can't use nextsame
here, because it'd be trying to nextsame
the Promise's block instead of our original routine.
Wrapped routines
Besides those already mentioned above, re-dispatch is helpful in many more situations. For instance, for dispatching to wrapped routines:
# enable wrapping:use soft;# function to be wrapped:sub square-root().wrap(sub ());say square-root(4); # OUTPUT: «2»say square-root(-4); # OUTPUT: «0+2i»
Routines of parent class
Another use case is to re-dispatch to methods from parent classes.
say Version.new('1.0.2') # OUTPUT: v1.0.2
is Versionsay LoggedVersion.new('1.0.2');# OUTPUT:# New version object created with arguments \("1.0.2")# v1.0.2
Coercion types
Coercion types force a specific type for routine arguments while allowing the routine itself to accept a wider input. When invoked, the arguments are narrowed automatically to the stricter type, and therefore within the routine the arguments have always the desired type.
In the case the arguments cannot be converted to the stricter type, a Type Check error is thrown.
sub double(Int(Cool) )say double '21';# OUTPUT: «42»say double 21; # OUTPUT: «42»say double Any; # Type check failed in binding $x; expected 'Cool' but got 'Any'
In the above example, the Int is the target type to which the argument $x
will be coerced, and Cool is the type that the routine accepts as wider input.
If the accepted wider input type is Any, it is possible to abbreviate the coercion Int(Any)
by omitting the Any
type, thus resulting in Int()
.
The coercion works by looking for a method with the same name as the target type: if such method is found on the argument, it is invoked to convert the latter to the expected narrow type. From the above, it is clear that it is possible to provide coercion among user types just providing the required methods:
# wants a Bar, but accepts Anysub print-bar(Bar() )print-bar Foo.new;
In the above code, once a Foo
instance is passed as argument to print-bar
, the Foo.Bar
method is called and the result is placed into $bar
.
Coercion types are supposed to work wherever types work, but Rakudo currently (2018.05) only implements them in signatures, for both parameters and return types.
Coercion also works with return types:
sub are-equal (Int , Int --> Bool(Int) ) ;for (2,4) X (1,2) -> (,)# OUTPUT: «Are 2 and 1 equal? TrueAre 2 and 2 equal? FalseAre 4 and 1 equal? TrueAre 4 and 2 equal? True»
In this case, we are coercing an Int
to a Bool
, which is then printed (put into a string context) in the for
loop that calls the function.
sub MAIN
Declaring a sub MAIN
is not compulsory in Perl 6 scripts, but you can provide one to create a command line interface for your script.