During Raku development, new features are often made available for users as experimental before their design is completed. Eventually these features may be made part of the Raku specification. To use these features, one uses the experimental
pragma in program source code, for example, like this:
use experimental :macros;
These are the features that, for the time being, are experimental.
pack §
Pack is a feature that allows binary serialization of general data structures, and is inherited from Perl's pack. The pack
order creates a Buf
by packing data structures in a certain way given by a packing string with the options shown in the description of unpack
. You turn it on by inserting this pragma at the beginning of your program:
use experimental :pack;
For instance, we can pack numbers interpreting them as hexadecimal (H
) with the pattern repeating until there are no more elements (*
):
use experimental :pack;say pack("H*", "414243").contents;# OUTPUT: «(65 66 67)»
There is a corresponding unpack
routine that does exactly the opposite.
use experimental :pack;my =Buf.new(65,66,67);say .unpack("H*"); # OUTPUT: «414243»
Not all of the symbols above are guaranteed to be implemented, and the roadmap does not include a fixed date for getting out of that stage.
Please see also documentation for pack
and unpack
in the Blob
page.
macros §
Macros are code-generating routines, that generate code in compile time, before the program is executed. In Raku its use is still experimental and it needs to be turned on via the pragma
use experimental :macros;
Macro processing happens during parsing time. A macro generates an abstract syntax tree, which is grafted into the program syntax tree. quasi
is the routine that performs this task.
macro does-nothing() { quasi {} }; does-nothing; # OUTPUT: «»
Macros are a kind of routine, so they can take arguments in exactly the same way, and act also in almost the same way.
macro is-mighty( $who ) { quasi { "$who is mighty!"} }; say is-mighty "Freija"; # OUTPUT: « "Freija" is mighty!»
"Almost" accounts for the fact that the argument is inserted as a literal, including the quotes. Please note that we can also eliminate the parentheses for a macro call, following the same rules as a routine. You can use the unquoting construct {{{}}}
to get rid of this kind of thing:
macro is-mighty( $who ) { quasi { {{{$who}}} ~ " is mighty!"} }; say is-mighty "Freija"; # OUTPUT: «Freija is mighty!»
Since macro expansion happens at parse time, care must be taken when using external variables in them:
use experimental :macros; my $called; macro called() { $called++; quasi { "Called" } }; say called() ~ " $called times"; say called() ~ " $called times"; # OUTPUT: «Called 2 timesCalled 2 times»
Since macros are expanded at parse time, $called
will be the result when runtime starts, which is already 2
, as printed. Initializing $called
with 0, however, will make this print Called 0 times
since that initialization is run after the parse phase, when the macros are expanded.
Macros are terribly useful when complicated, computed initializations need to be done. However, they are still in the experimental nursery for a good reason. Although the features shown above are not very likely to change, anything, even their very presence, might change at any one time depending in necessities, so it would be best to keep them away from production code. Meanwhile, taking a look at this article by Masak as well as 007
, a new macro language, might provide a glimpse into the things to come.
cached §
The following pragma:
use experimental :cached;
turns on the is cached
trait, which stores the result of a routine call, returning the same value if called with the same arguments.
It can be used when heavy calculations are involved, as in this sample that uses amicable numbers, taken from the 2018 Advent Calendar:
use experimental :cached; sub aliquot-parts( $number ) is cached { (^$number).grep: $number %% *; } sub infix:<amic>( $m, $n ) { $m == aliquot-parts($n).sum && $n == aliquot-parts($m).sum; } # Taken from https://en.wikipedia.org/wiki/Amicable_numbers my @numbers = [2620, 2924, 5020, 5564, 6232, 6368, 66928, 66992]; say "Aliquot parts of $_ are ", aliquot-parts $_ for @numbers; for @numbers X @numbers -> @pair { say "@pair[0] and @pair[1] are ", @pair[0] amic @pair[1]??" "!!"not ", "amicable"; }
This code caches the computation of the aliquot parts, so that when the amic
operator is called, it's only computed once; as a matter of fact, the first loop which prints these aliquot parts will be the only one that actually perform the computation.
See also the description of the trait for additional information and examples.