In Functions§
See primary documentation in context for 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 $reason, Str $name, |) {*} multi congratulate($reason, $name) { say "Hooray for your $reason, $name"; } multi congratulate($reason, $name, Int $rank) { say "Hooray for your $reason, $name -- got rank $rank!"; } congratulate('being a cool number', 'Fred'); # OK congratulate('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 &congratulate.signature # OUTPUT: «(Str $reason, Str $name, | is raw)»
You can give the proto
a function body, and place the {*}
(note there is no whitespace inside the curly braces) where you want the dispatch to be done. This can be useful when you have a "hole" in your routine that gives it different behavior depending on the arguments given:
# attempts to notify someone -- False if unsuccessful proto notify(Str $user, Str $msg) { my \hour = DateTime.now.hour; if 8 < hour < 22 { return {*}; } else { # we can't notify someone when they might be sleeping return False; } }
Since proto
is a wrapper for multi
candidates, the signatures of the routine's multi
candidates do not necessarily have to match that of the proto
; arguments of multi
candidates may have subtypes of those of the proto
, and the return types of the multi
candidates may be entirely different from that of the proto
. Using differing types like this is especially useful when giving proto
a function body:
enum DebugType <LOG WARNING ERROR>; #|[ Prints a message to stderr with a color-coded key. ] proto debug(DebugType:D $type, Str:D $message --> Bool:_) { note sprintf qb/\e[1;%dm[%s]\e[0m %s/, {*}, $type.key, $message } multi debug(LOG;; Str:D --> 32) { } multi debug(WARNING;; Str:D --> 33) { } multi debug(ERROR;; Str:D --> 31) { }
{*}
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() $str, Int $number = 42) {*} multi mistake-proto($str, $number) { say $str.^name } mistake-proto(7, 42); # OUTPUT: «Int» -- not passed on
mistake-proto('test'); # fails -- not passed on
A longer example using proto
for methods shows how to extract common functionality into a proto method.
class NewClass { has $.debug is rw = False; has $.value is rw = 'Initial value'; proto method handle( | ) { note "before value is 「$.value」" if $.debug; {*} note "after value is 「$.value」" if $.debug; } multi method handle(Str $s) { $.value = $s; say 'in string' } multi method handle(Positional $s) { $.value = $s[0]; say 'in positional' } multi method handle( $a, $b ) { $.value = "$a is looking askance at $b"; say 'with more than one value' } } my NewClass $x .= new; $x.handle('hello world'); $x.handle(<hello world>); $x.debug = True; $x.handle('hello world'); $x.handle(<hello world>); $x.handle('Claire', 'John'); # OUTPUT: # in string # in positional # before value is 「hello」 # in string # after value is 「hello world」 # before value is 「hello world」 # in positional # after value is 「hello」 # before value is 「hello」 # with more than one value # after value is 「Claire is looking askance at John」