Movatterモバイル変換


[0]ホーム

URL:


Functions

Functions and functional programming in Raku

Routines are one of the means Raku has to reuse code. They come in several forms, most notablyMethods, which belong in classes and roles and are associated with an object; and functions (also calledsubroutines orSubs, 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 aSignature, also calledparameter 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 viaRoutine.

Defining/Creating/Using functions§

Subroutines§

The basic way to create a subroutine is to use thesub declarator followed by an optionalidentifier:

submy-func {say"Look ma, no args!" }my-func;

The sub declarator returns a value of typeSub that can be stored in any container:

my&c=sub {say"Look ma, no name!" }c;# OUTPUT: «Look ma, no name!␤»myAny:D$f=sub {say'Still nameless...' }$f();# OUTPUT: «Still nameless...␤»myCode \a=sub {sayrawcontainersdon'timplementpostcircumfix:<( )>‘ };a.();# OUTPUT: «raw containers don't implement postcircumfix:<( )>␤»

The declaratorsub will declare a new name in the current scope at compile time. As such, any indirection has to be resolved at compile time:

constantaname='foo';sub ::(aname) {say'oi‽' };foo;

This will become more useful once macros are added to Raku.

To have the subroutine take arguments, aSignature goes between the subroutine's name and its body, in parentheses:

subexclaim ($phrase) {say$phrase~"!!!!"}exclaim"Howdy, World";

By default, subroutines arelexically scoped. That is,sub foo {...} is the same asmy sub foo {...} and is only defined within the current scope.

subescape($str) {# Puts a slash before non-alphanumeric charactersS:g[<-alpha -digit>]="\\$/"given$str}sayescape'foo#bar?';# OUTPUT: «foo\#bar\?␤»{subescape($str) {# Writes each non-alphanumeric character in its hexadecimal escapeS:g[<-alpha -digit>]="\\x[{$/.ord.base(16)}]"given$str    }sayescape'foo#bar?'# OUTPUT: «foo\x[23]bar\x[3F]␤»}# Back to original escape functionsayescape'foo#bar?';# OUTPUT: «foo\#bar\?␤»

Subroutines don't have to be named. If unnamed, they're calledanonymous subroutines.

saysub ($a,$b) {$a**2+$b**2 }(3,4)# OUTPUT: «25␤»

But in this case, it's often desirable to use the more succinctBlock syntax. Subroutines and blocks can be called in place, as in the example above.

say->$a,$b {$a**2+$b**2 }(3,4)# OUTPUT: «25␤»

Or even

say {$^a**2+$^b**2 }(3,4)# OUTPUT: «25␤»

Blocks and lambdas§

Whenever you see something like{ $_ + 42 },-> $a, $b { $a ** $b }, or{ $^text.indent($:spaces) }, that'sBlock syntax; the-> is considered also part of the block. Statements such asif,for,while are followed by these kind of blocks.

for1,2,3,4->$a,$b {say$a~$b;}# OUTPUT: «12␤34␤»

They can also be used on their own as anonymous blocks of code.

say {$^a**2+$^b**2}(3,4)# OUTPUT: «25␤»

Please note that this implies that, despite the fact that statements such asif do not define a topic variable, they actually can:

my$foo=33;if$foo**33->$a {say"$a is not null";#}# OUTPUT: «129110040087761027839616029934664535539337183380513 is not null␤»

For block syntax details, see the documentation for theBlock type.

Signatures§

The parameters that a function accepts are described in itssignature.

subformat(Str$s) {... }->$a,$b {... }

Details about the syntax and use of signatures can be found in thedocumentation on theSignature 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.

subs {say@_, %_};say&s.signature# OUTPUT: «(*@_, *%_)␤»

Arguments§

Arguments are supplied as a comma separated list. To disambiguate nested calls, use parentheses:

subf(&c){c()*2 };# call the function reference c with empty parameter listsubg($p){$p-2 };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:

subf(|c){};f :named(35);# A named argument (in "adverb" form)fnamed=>35;# Also a named argumentf :35named;# A named argument using abbreviated adverb formf'named'=>35;# Not a named argument, a Pair in a positional argumentf'hi', :1x, :2y;# Positional and named argumentsmy \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 aCapture container. Details about the syntax and use of these containers can be found inCapture's documentation.

When using named arguments, note that normal list "pair-chaining" allows one to skip commas between named arguments.

subf(|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.

If positional arguments are also passed, then either they must be passed within parentheses placedimmediately after the function's name or the comma after the last positional argument must be kept when chaining named arguments in abbreviated adverb form. The spaces between chained named arguments and the list of positional arguments is optional.

subp($x,$y,:$translate,:$rotate) {};p(1,1, :translate, :rotate);# normal wayp1,1, :translate, :rotate;# also normal wayp(1,1) :translate  :rotate;# parentheses + chained named argumentsp(1,1) :translate:rotate;p(1,1):translate:rotate;p1,1, :translate  :rotate;# dangling comma + chained named argumentsp1,1, :translate:rotate;p1,1,:translate:rotate;

Return values§

AnyBlock orRoutine will provide the value of its last expression as a return value to the caller. If eitherreturn orreturn-rw is called, then its parameter, if any, will become the return value. The default return value isNil.

suba {42 };subb {saya };subc { };b;# OUTPUT: «42␤»sayc;# OUTPUT: «Nil␤»

Multiple return values are returned as a list or by creating aCapture. Destructuring can be used to untangle multiple return values.

suba {42,'answer' };puta.raku;# OUTPUT: «(42, "answer")␤»my ($n,$s)=a;put[$s,$n];# OUTPUT: «answer 42␤»subb {<a b c>.Capture };putb.raku;# OUTPUT: «\("a", "b", "c")␤»

Return type constraints§

Raku has many ways to specify a function's return type:

subfoo(-->Int)      {};say&foo.returns;# OUTPUT: «(Int)␤»
subfoo()returnsInt {};say&foo.returns;# OUTPUT: «(Int)␤»
subfoo()ofInt      {};say&foo.returns;# OUTPUT: «(Int)␤»
myIntsubfoo()      {};say&foo.returns;# OUTPUT: «(Int)␤»

Attempting to return values of another type will cause a compilation error.

subfoo()returnsInt {"a"; };foo;# Type check fails

returns andof are equivalent, and both take only a Type since they are declaring a trait of theCallable. 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 thatNil andFailure are exempt from return type constraints and can be returned from any routine, regardless of its constraint:

subfoo()returnsInt {fail   };foo;# Failure returnedsubbar()returnsInt {return };bar;# Nil returned

Multi-dispatch§

Raku allows for writing several routines with the same name but different signatures. When the routine is called by name, the runtime environment determines the propercandidate and invokes it.

Each candidate is declared with themulti keyword. Dispatch happens depending on the parameterarity (number), type and name; and under some circumstances the order of the multi declarations. Consider the following example:

# version 1multihappy-birthday($name ) {say"Happy Birthday$name !";}# version 2multihappy-birthday($name,$age ) {say"Happy{$age}th Birthday$name !";}# version 3multihappy-birthday(:$name,:$age,:$title='Mr' ) {say"Happy{$age}th Birthday$title$name !";}# 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 thehappy-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 anothermulti 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:

multihappy-birthday(Str$name,Int$age ) {say"Happy{$age}th Birthday$name !";}multihappy-birthday(Str$name,Str$title ) {say"Happy Birthday$title$name !";}multihappy-birthday(Str:$name,Int:$age ) {say"Happy Birthday$name, you turned$age !";}happy-birthday'Luca',40;# OUTPUT: «Happy 40th Birthday Luca !␤»happy-birthday'Luca','Mr';# OUTPUT: «Happy Birthday Mr Luca !␤»happy-birthdayage=>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 onSignature literals.

multias-json(Bool$d) {$d??'true'!!'false'; }multias-json(Real$d) {~$d }multias-json(@d)      {sprintf'[%s]',@d.map(&as-json).join(',') }sayas-json(True );# OUTPUT: «true␤»sayas-json(10.3 );# OUTPUT: «10.3␤»sayas-json([True,10.3,False,24] );# OUTPUT: «[true, 10.3, false, 24]␤»

For some signature differences (notably when using a where clause or a subset) the order of definition of the multi methods or subs is used, evaluating each possibility in turn. Seemulti resolution by order of definition below for examples.

multi without any specific routine type always defaults to asub, but you can use it on methods as well. The candidates are all the multi methods of the object:

classCongrats {multimethodcongratulate($reason,$name) {say"Hooray for your$reason,$name";    }}roleBirthdayCongrats {multimethodcongratulate('birthday',$name) {say"Happy birthday,$name";    }multimethodcongratulate('birthday',$name,$age) {say"Happy{$age}th birthday,$name";    }}my$congrats=Congrats.newdoesBirthdayCongrats;$congrats.congratulate('promotion','Cindy');# OUTPUT: «Hooray for your promotion, Cindy␤»$congrats.congratulate('birthday','Bob');# OUTPUT: «Happy birthday, Bob␤»

Unlikesub, 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.

Multi-dispatch can also work on parameter traits, with routines withis rw parameters having a higher priority than those that do not:

protoþoo (|) {*}multiþoo($ðarisrw ) {$ðar=42 }multiþoo($ðar ) {$ðar+42 }my$bar=7;sayþoo($bar);# OUTPUT: «42␤»

proto§

proto is a way to formally declare commonalities betweenmulti candidates. It acts as a wrapper that can validate but not modify arguments. Consider this basic example:

protocongratulate(Str$reason,Str$name,|) {*}multicongratulate($reason,$name) {say"Hooray for your$reason,$name";}multicongratulate($reason,$name,Int$rank) {say"Hooray for your$reason,$name -- got rank$rank!";}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 allmulti congratulate subs conform to the basic signature of two strings, optionally followed by further parameters. The| is an un-namedCapture parameter, and allows amulti to take additional arguments. The first two calls succeed, but the third fails (at compile time) because42 doesn't matchStr.

say&congratulate.signature# OUTPUT: «(Str $reason, Str $name, | is raw)␤»

You can give theproto 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 unsuccessfulprotonotify(Str$user,Str$msg) {my \hour=DateTime.now.hour;if8<hour< 22 {      return {*};   } else {      # we can't notify someone when they might be sleeping      return False;   }}

Sinceproto is a wrapper formulti candidates, the signatures of the routine'smulti candidates do not necessarily have to match that of theproto; arguments ofmulti candidates may have subtypes of those of theproto, and the return types of themulti candidates may be entirely different from that of theproto. Using differing types like this is especially useful when givingproto a function body:

enumDebugType<LOG WARNING ERROR>;#|[ Prints a message to stderr with a color-coded key.]protodebug(DebugType:D$type,Str:D$message-->Bool:_) {notesprintfqb/\e[1;%dm[%s]\e[0m%s/, {*},$type.key,$message}multidebug(LOG;;Str:D-->32)     { }multidebug(WARNING;;Str:D-->33) { }multidebug(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.

protomistake-proto(Str()$str,Int$number=42) {*}multimistake-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 usingproto for methods shows how to extract common functionality into a proto method.

classNewClass {has$.debugisrw=False;has$.valueisrw='Initial value';protomethodhandle(| ) {note"before value is 「$.value"if$.debug;        {*}note"after value is 「$.value"if$.debug;    }multimethodhandle(Str$s) {$.value=$s;say'in string'    }multimethodhandle(Positional$s) {$.value=$s[0];say'in positional'    }multimethodhandle($a,$b ) {$.value="$a is looking askance at$b";say'with more than one value'    }}myNewClass$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」

only§

Theonly keyword precedingsub ormethod indicates that it will be the only function with that name that inhabits a given namespace.

onlysubyou () {"Can make all the world seem right"};

This will make other declarations in the same namespace, such as

subyou ($can ) {"Make the darkness bright" }

fail with an exception of typeX::Redeclaration.only is the default value for all subs; in the case above, not declaring the first subroutine asonly will yield exactly the same error; however, nothing prevents future developers from declaring a proto and preceding the names withmulti. Usingonly before a routine is adefensive 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.rakuRedeclaration of routine 'you' (did you mean to declare a multi-sub?)at /tmp/only-redeclaration.raku:3------> <BOL>⏏<EOL>

Anonymous subs cannot be declaredonly.only sub {} will throw an error of type, surprisingly,X::Anon::Multi.

multi resolution by order of definition§

When the breakdown by parameter type is not enough to find an unambiguous match, there are some different tiebreakers that may be evaluated in order of declaration of the methods or subs: these include where clauses and subsets, named parameters, and signature unpacks.

In this code example, two multi subs are distinguished only by where clauses where there's one ambiguous case that might pass either of them, the value 4. In this case, which ever multi sub is defined first wins:

{multicheck_range (Int$nwhere {$_>3} ) {return"over 3";  };multicheck_range (Int$nwhere {$_<5} ) {return"under 5";  };saycheck_range(4);# OUTPUT: over 3}{multicheck_range (Int$nwhere {$_<5} ) {return"under 5";  };multicheck_range (Int$nwhere {$_>3} ) {return"over 3";  };saycheck_range(4);# OUTPUT: under 5}

In the following example, three subsets are used to restrict strings to certain allowed values, where there are overlaps between all three:

subsetMonsterofStrwhere {$_eqany(<godzilla gammera ghidora> ) };subsetHeroofStrwhere {$_eqany(<godzilla ultraman inframan> ) };subsetKnockoffofStrwhere {$_eqany(<gammera inframan thorndike> ) };{multispeak (Monster$name) {say"The monster,$name roars!";    }multispeak (Hero$name) {say"The hero,$name shouts!";    }multispeak (Knockoff$name) {say"The knockoff,$name, slinks away...";    }speak('godzilla');# OUTPUT: The monster, godzilla roars!speak('gammera');# OUTPUT: The monster, gammera roars!speak('inframan');# OUTPUT: The hero, inframan shouts!}

Note that here 'godzilla' is treated as Monster, not as Hero, because the Monster multi comes first; and neither 'gammera' or 'inframan' are treated as Knockoff, because that multi comes last.

It should be noted that the order of definition is the order in which Raku sees them, which might not be easy to discern if, for example, the multi subs were imported from different modules. As the organization of a code base becomes more complex, object classes may scale better than using subsets as types, as in this example.

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 ofpush).

If you wish to match this look and feel, anyIterable argument must be broken out element-by-element using a**@ slurpy, with two nuances:

This can be achieved by using a slurpy with a+ or+@ instead of**@:

subgrab(+@a) {"grab$_".sayfor@a }

which is shorthand for something very close to:

multigrab(**@a) {"grab$_".sayfor@a }multigrab(\a) {a~~Iterableanda.VAR!~~Scalar??nextwith(|a)!!nextwith(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 1␤grab 2␤»grab((1,2));# OUTPUT: «grab 1␤grab 2␤»grab($(1,2));# OUTPUT: «grab 1 2␤»grab((1,2),3);# OUTPUT: «grab 1 2␤grab 3␤»

This also makes user-requested flattening feel consistent whether there is one sublist, or many:

grab(flat (1,2), (3,4));# OUTPUT: «grab 1␤grab 2␤grab 3␤grab 4␤»grab(flat$(1,2),$(3,4));# OUTPUT: «grab 1 2␤grab 3 4␤»grab(flat (1,2));# OUTPUT: «grab 1␤grab 2␤»grab(flat$(1,2));# OUTPUT: «grab 1␤grab 2␤»

It's worth noting that mixing binding and sigilless variables in these cases requires a bit of finesse, because there is noScalar intermediary used during binding.

my$a= (1,2);# Normal assignment, equivalent to $(1, 2)grab($a);# OUTPUT: «grab 1 2␤»my$b:= (1,2);# Binding, $b links directly to a bare (1, 2)grab($b);# OUTPUT: «grab 1␤grab 2␤»my \c= (1,2);# Sigilless variables always bind, even with '='grab(c);# OUTPUT: «grab 1␤grab 2␤»

See thedocumentation of thelist subroutine for more examples of how to use a routine that adheres to the single argument rule.

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$square=sub (Numeric$x) {$x*$x }# and then use it:say$square(6);# OUTPUT: «36␤»

Or you can reference an existing named function by using the&-sigil in front of it.

subsquare($x) {$x*$x };# get hold of a reference to the function:my$func=&square

This is very useful forhigher order functions, that is, functions that take other functions as input. A simple one ismap, which applies a function to each input element:

subsquare($x) {$x*$x };my@squared=map&square,1..5;sayjoin',',@squared;# OUTPUT: «1, 4, 9, 16, 25␤»

You can use the same for operators, except that in that case the name with which they have been declared, usinginfix:, must be used:

my$exp:=&infix:<**>;say$exp(7,3);# OUTPUT: «343␤»

This can be done even in cases where operators have beenauto-generated, for instance in this case whereXX is the metaoperatorX applied to theX operator.

my$XX:=&infix:<XX>;say$XX([1,(2,3)],[(4,5),6]  );# OUTPUT: «(((1 (4 5))) ((1 6)) (((2 3) (4 5))) (((2 3) 6)))␤»

Baseline is that, in case of operators, you don't really need to worry about the actual way they were defined, just use the&infix< > to grab a pointer to them.

Infix form§

To call a subroutine with 2 arguments like an infix operator, use a subroutine reference surrounded by[ and].

subplus {$^a+$^b };say21[&plus]21;# OUTPUT: «42␤»

Closures§

All code objects in Raku areclosures, which means they can reference lexical variables from an outer scope.

subgenerate-sub($x) {my$y=2*$x;returnsub {say$y };#      ^^^^^^^^^^^^^^  inner sub, uses $y}my$generated=generate-sub(21);$generated();# OUTPUT: «42␤»

Here,$y is a lexical variable insidegenerate-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 itclosed over the variable.

Another closure example is the use ofmap to multiply a list of numbers:

my$multiply-by=5;sayjoin',',map {$_*$multiply-by },1..5;# OUTPUT: «5, 10, 15, 20, 25␤»

Here, the block passed tomap 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 asmap.

Routines§

Routines are code objects that conform to the typeRoutine, most notablySub,Method,Regex andSubmethod.

They carry extra functionality in addition to what aBlock supplies: they can come asmultis, you canwrap them, and exit early withreturn:

my$keywords=set<if for unless while>;subhas-keyword(*@words) {for@words->$word {returnTrueif$word(elem)$keywords;    }False;}sayhas-keyword'not','one','here';# OUTPUT: «False␤»sayhas-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 toreturn, they attach to the outermost routine.

Routines can be inlined and as such provide an obstacle for wrapping. Use the pragmause soft; to prevent inlining to allow wrapping at runtime.

subtestee(Int$i,Str$s){rand.Rat*$i~$s;}subwrap-to-debug(&c){say"wrapping{&c.name} with arguments{&c.signature.raku}";&c.wrap:sub (|args){note"calling{&c.name} with{args.gist}";my \ret-val:=callwith(|args);note"returned from{&c.name} with return value{ret-val.raku}";ret-val    }}my$testee-handler=wrap-to-debug(&testee);# OUTPUT: «wrapping testee with arguments :(Int $i, Str $s)␤»saytestee(10,"ten");# OUTPUT: «calling testee with \(10, "ten")␤returned from testee with return value "6.151190ten"␤6.151190ten␤»&testee.unwrap($testee-handler);saytestee(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). An expanded explanation of all these operators and what they mean is includedin this table.

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 viause orimport also makes it available.

# adding a multi candidate to an existing operator:multiinfix:<+>(Int$x,"same") {2*$x };say21+"same";# OUTPUT: «42␤»# defining a new operatorsubpostfix:<!>(Int$xwhere {$x>=0 }) {[*]1..$x };say6!;# OUTPUT: «720␤»

The operator declaration becomes available as soon as possible, so you can recurse into a just-defined operator:

subpostfix:<!>(Int$xwhere {$x>=0 }) {$x==0??1!!$x* ($x-1)!}say6!;# OUTPUT: «720␤»

Circumfix and postcircumfix operators are made of two delimiters, one opening and one closing.

subcircumfix:<START END>(*@elems) {"start",@elems,"end"}saySTART'a','b','c'END;# OUTPUT: «(start [a b c] end)␤»

Postcircumfixes also receive the term after which they are parsed as an argument:

subpostcircumfix:<!! !!>($left,$inside) {"$left -> ($inside )"}say42!!1!!;# OUTPUT: «42 -> ( 1 )␤»

Blocks can be assigned directly to operator names. Use a variable declarator and prefix the operator name with an&-sigil.

my&infix:<ieq>=->|l {[eq]l>>.fc };say"abc"ieq"Abc";# OUTPUT: «True␤»

Precedence§

Operator precedence in Raku is specified relative to existing operators. The traitsis tighter,is equiv andis 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 thaninfix:<+>, and squeezing one in between works like this:

subinfix:<!!>($a,$b)istighter(&infix:<+>) {2* ($a+$b)}say1+2*3!!4;# OUTPUT: «21␤»

Here, the1 + 2 * 3 !! 4 is parsed as1 + ((2 * 3) !! 4), because the precedence of the new!! operator is between that of+ and*.

The same effect could have been achieved with:

subinfix:<!!>($a,$b)islooser(&infix:<*>) {... }

To put a new operator on the same precedence level as an existing operator, useis 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+ ismathematically associative.

But for other operators it matters a great deal. For example, for the exponentiation/power operator,infix:<**>:

say2** (2**3);# OUTPUT: «256␤»say (2**2)**3;# OUTPUT: «64␤»

Raku has the following possible associativity configurations:

AssociativityMeaning of $a ! $b ! $c
left($a ! $b) ! $c
right$a ! ($b ! $c)
nonILLEGAL
chain($a ! $b) and ($b ! $c)
listinfix:<!>($a; $b; $c)

TheOperator docs contain additional details about operator associativity.

You can specify the associativity of an infix operator with theis assoc trait, whereleft is the default associativity. Specifying the associativity of non-infix operators is planed but not yet implemented.

subinfix:<§>(*@a)isassoc<list> {'('~@a.join('|')~')';}say1 §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:

classChildClassisParentClass {... }#                ^^ trait, with argument ParentClasshas$.attribisrw;#            ^^^^^  trait with name 'rw'classSomeClassdoesAnotherRole {... }#               ^^^^ traithas$!another-attributehandles<close>;#                       ^^^^^^^ trait

... and alsois tighter,is looser,is equiv andis assoc from the previous section.

Traits are subs declared in the formtrait_mod<VERB>, whereVERB stands for the name likeis,does orhandles. It receives the modified thing as argument, and the name as a named argument. SeeSub for details.

multitrait_mod:<is>(Routine$r,:$doubles!) {$r.wrap({2*callsame;    });}subsquare($x)isdoubles {$x*$x;}saysquare3;# OUTPUT: «18␤»

Seetype 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 awrap.

Fortunately, we have a series of re-dispatching tools that help us to make it easy.

The Next Tools§

The table below represents the next tools in relation to three attributes:

  • Returns: "Yes" means that it returns, and the code will continue from that point. "No" means that it never returns to this function -- it's as though the call has a built-in "return".

  • Arguments: Specifically, whether the arguments from the current function are reused in the call, or whether you need to specify some arguments. In the case ofnextcallee, there are no arguments needed, because they're instead passed to the return value when it's called.

  • Referee: This is the method that will be called by the tool (the reciprocal of "referrer"). It can be one of:

    • next: The next item in the chain

    • self: It will call itself again, as in recursion

    • preset: It will call the item that was saved bynextcallee

ReturnsArgumentsReferee*
nextsameNoReusenext
nextwithNoSpecifynext
callsameYesReusenext
callwithYesSpecifynext
nextcalleeYesNonenext
samewithYesSpecifyself
nextcallee's rv*YesSpecifypreset

*nextcallee's rv is the tool returned from callingnextcallee, which itself can be called.

From the above we learn:

  • with always means that the arguments need to be specified -- this one is consistent.

  • same at word end means the arguments of the currently running function will automatically be reused when calling the referee. Note thatsamewith, despite containing "same", doesn't mean that the arguments will be reused. That's because the "same" here refers to the Referee -- the function is calling the same method as itself, not the next in the call stack.

  • next means that it won't return, except fornextcallee, which does return

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.

protoa(|) {*}multia(Any$x) {say"Any$x";return5;}multia(Int$x) {say"Int$x";my$res=callsame;say"Back in Int with$res";}a1;# OUTPUT: «Int 1␤Any 1␤Back 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.

protoa(|) {*}multia(Any$x) {say"Any$x";return5;}multia(Int$x) {say"Int$x";my$res=callwith($x+1);say"Back in Int with$res";}a1;# OUTPUT: «Int 1␤Any 2␤Back in Int with 5␤»

Here,a 1 calls the most specificInt candidate first, andcallwith re-dispatches to the less specificAny candidate. Note that although our parameter$x + 1 is anInt, still we call the next candidate in the chain.

In this case, for example:

protohow-many(|) {*}multihow-many(Associative$a ) {say"Associative$a";my$calling=callwith(1=>$a );return$calling;}multihow-many(Pair$a ) {say"Pair$a";return"There is$a"}multihow-many(Hash$a ) {say"Hash$a";return"Hashing$a";}my$little-piggy=little=>'piggy';say$little-piggy.^name;# OUTPUT: «Pair␤»say&how-many.cando( \($little-piggy ));# OUTPUT: «(sub how-many (Pair $a) { #`(Sub|68970512) ... } sub how-many (Associative $a) { #`(Sub|68970664) ... })␤»sayhow-many($little-piggy  );# OUTPUT: «Pair little     piggy␤There is little piggy␤»

the only candidates that take thePair argument supplied by the user are the two functions defined first. Although aPair can be easily coerced to aHash, here is how signatures match:

say:(Pair )~~:(Associative );# OUTPUT: «True␤»say:(Pair )~~:(Hash );# OUTPUT: «False␤»

The arguments provided by us are aPair. It does not match aHash, 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 andnever returns.

protoa(|) {*}multia(Any$x) {say"Any$x";return5;}multia(Int$x) {say"Int$x";nextsame;say"never executed because nextsame doesn't return";}a1;# OUTPUT: «Int 1␤Any 1␤»

sub nextwith§

nextwith calls the next matching candidate with arguments provided by users andnever returns.

protoa(|) {*}multia(Any$x) {say"Any$x";return5;}multia(Int$x) {say"Int$x";nextwith($x+1);say"never executed because nextwith doesn't return";}a1;# OUTPUT: «Int 1␤Any 2␤»

sub samewith§

samewith calls the multi again with arguments provided at the call site and returns the value provided by the call. This can be used for self-recursion.

protofactorial(|) {*}multifactorial(Int$xwhere*1) {1 }multifactorial(Int$x) {$x*samewith($x-1);}say (factorial10);# OUTPUT: «36288000␤»

sub nextcallee§

Redispatch may be required to call a block that is not the current scope what providesnextwith and friends with the problem to referring to the wrong scope. Usenextcallee to capture the right candidate and call it at the desired time.

protopick-winner(|) {*}multipick-winner (Int \s) {my&nextone=nextcallee;Promise.in(π²).then: {nextones }}multi pick-winner { say "Woot! $^w won" }with pick-winner ^5 .pick -> \result {    say "And the winner is...";    await result;}# OUTPUT:# And the winner is...# Woot! 3 won

TheInt candidate takes thenextcallee and then fires up aPromise to be executed in parallel, after some timeout, and then returns. We can't usenextsame here, because it'd be trying tonextsame the Promise's block instead of our original routine.

Note that, despite its name, thenextcallee function is like:

  • callwith/nextwith, in that it takes parameters

  • callwith/callsame, in that it returns; the call tonextcallee returns a reference, and the call to the reference also returns (unlikenextwith/nextsame, which don't return)

Wrapped routines§

Besides those already mentioned above, re-dispatch is helpful in many more situations. For instance, for dispatching to wrapped routines:

# enable wrapping:usesoft;# function to be wrapped:subsquare-root($x) {$x.sqrt }&square-root.wrap(sub ($num) {nextsameif$num>=0;1i*callwith(abs($num));});saysquare-root(4);# OUTPUT: «2␤»saysquare-root(-4);# OUTPUT: «0+2i␤»

Routines of parent class§

Another use case is to re-dispatch to methods from parent classes.

sayVersion.new('1.0.2')# OUTPUT: v1.0.2
classLoggedVersionisVersion {methodnew(|c) {note"New version object created with arguments"~c.raku;nextsame;    }}sayLoggedVersion.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, aType Check error is thrown.

subdouble(Int(Cool)$x) {2*$x}saydouble'21';# OUTPUT: «42␤»saydouble21;# OUTPUT: «42␤»saydoubleAny;# Type check failed in binding $x; expected 'Cool' but got 'Any'

In the above example, theInt is the target type to which the argument$x will be coerced, andCool is the type that the routine accepts as wider input.

If the accepted wider input type isAny, it is possible to abbreviate the coercionInt(Any) by omitting theAny type, thus resulting inInt().

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:

classBar {has$.msg;}classFoo {has$.msg="I'm a foo!";# allows coercion from Foo to BarmethodBar {Bar.new(:msg($.msg~' But I am now Bar.'));   }}# wants a Bar, but accepts Anysubprint-bar(Bar()$bar) {say$bar.^name;# OUTPUT: «Bar␤»say$bar.msg;# OUTPUT: «I'm a foo! But I am now Bar.␤»}print-barFoo.new;

In the above code, once aFoo instance is passed as argument toprint-bar, theFoo.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:

subare-equal (Int$x,Int$y-->Bool(Int) ) {$x-$y };for (2,4)X (1,2)-> ($a,$b) {say"Are$a and$b equal?",are-equal($a,$b);}#  OUTPUT: «Are 2 and 1 equal? True␤Are 2 and 2 equal? False␤Are 4 and 1 equal? True␤Are 4 and 2 equal? True␤»

In this case, we are coercing anInt to aBool, which is then printed (put into a string context) in thefor loop that calls the function.

sub MAIN§

Declaring asub MAIN is not compulsory in Raku scripts, but you can provide one to create acommand line interface for your script.


[8]ページ先頭

©2009-2026 Movatter.jp