CompUnit::Util
CompUnit::Util
Utility functions for introspecting CompUnits and re-exporting their symbols.
CompUnit::Util contains set of utilities to introspect CompUnit
stuff and a bunch of compile time symbol manipulation tools. Its main
purpose is to encapsulate the compiler hacks needed to implement
features like 're-exporting' which don't exist in rakudo yet.
The API should now be stable enough to use.
warning this module relies on unspec'd rakudo internals and can (and has and will) break without warning.
CompUnit Utilities
CompUnit introspection utilities.
Apart from load none of the routines here will load a compunit. All
parameters named $handle are converted to a CompUnit::Handle
automatically. If the $handle you pass is not a defined CompUnit
or CompUnit::Handle, &find-loaded will be used to search for an
loaded compunit matching it.
warning because of RT
#127302, you
should be very careful about manipulating CompUnit and
CompUnit::Handle objects within BEGIN blocks. CompUnit::Handles
cannot be serialized at the moment. For example,
use CompUnit::Util :load;
BEGIN load('SomeModule');should be written as
use CompUnit::Util :load;
BEGIN {
load('SomeModule');
Nil;
}load
(Str:D $short-name,*%opts --> CompUnit:D)
use CompUnit::Util :load;
my CompUnit $cu = load('Test');
# or even
my $cu = load('MyModule', version => v3);Loads a compunit by name. All named arguments to
CompUnit::DependencySpecification are accepted (other than
short-name which is the positional argument). At the moment load
is just short for:
$*REPO.need(CompUnit::Dependencyspecification.new(:short_name<MyModule>));find-loaded
($match --> CompUnit)
use CompUnit::Util :find-loaded;
need SomeModule;
my CompUnit $some-module = find-loaded('SomeModule');Searches all the CompUnit::Repositorys until it finds a loaded
compunit matching $match. Returns a failure otherwise.
all-loaded
use CompUnit::Util :all-loaded;
.note for all-loaded;Returns all loaded CompUnits.
at-unit
($handle,Str:D $key)
use CompUnit::Util :at-unit;
say at-unit('CompUnit::Util','$=pod');Gets a symbol from the UNIT scope of the compunit. If you want to do
this at compile time while a compunit is loading see get-unit.
unit-to-hash
($handle)
use CompUnit::Util :unit-to-hash;
my %unit := unit-to-hash('SomeModule');returns a Hash representing the UNIT:: of the module.
capture-import
($handle, *@pos, *%named --> Hash:D)
use CompUnit::Util :capture-import;
need SomeModule;
my %symbols = capture-import('SomeModule',:tag);Attempts to simulate a use statement. Returns a hash of all the
symbols the compunit would export if it were used.
WHO Utilities
set-in-WHO
($WHO,$key,$value)
use CompUnit::Util :who;
my package Example {};
BEGIN set-in-WHO(Example.WHO,'Foo::Bar::$Baz','win');
say Example::Foo::Bar::<$Baz>; #-> winConvenience routine for setting a symbol's value inside a package that might not exist yet. Only useful outside the compunit being compiled.
descend-WHO
($WHO,Str:D $path)
use CompUnit::Util :who;
my package Example {};
BEGIN set-in-WHO(Example.WHO,'Foo::Bar::Baz','win');
BEGIN note descend-WHO(Example.WHO,'Foo::Bar::Baz'); #-> winConvenience routine for getting a symbol's value with a path from a
Stash like .WHO. Only useful outside the currently compiling
compunit (where you can just use the normal syntax).
Re-Exporting
The following routines provide re-exporting which is not yet implemented in rakudo.
re-export
($handle)
use CompUnit::Util :re-export;
need SomeModule;
BEGIN re-export('SomeModule');
# This compunit will now export everything that SomeModule doesMerges the EXPORT package from $handle into the
present UNIT::EXPORT.
this routine can only be called at BEGIN time
re-exporthow
($handle)
use CompUnit::Util :re-export;
need SomeModule;
BEGIN re-exporthow('SomeModule');
# This compunit now exports SomeModule's custom declaratorsMerges the EXPORTHOW from $handle into the present
UNIT::EXPORTHOW. UNIT::EXPORTHOW will be created if it doesn't
exist but it won't clobber it if it does.
this routine can only be called at BEGIN time
warning Using this is throwing up warnings for some reason.
steal-export-sub
($handle)
use CompUnit::Util :re-export;
need SomeModule;
BEGIN steal-export-sub('SomeModule');
# This compunit now has the same &EXPORT as SomeModuleSets UNIT::<&EXPORT> to $handle's &EXPORT.
this routine can only be called at BEGIN time
steal-globalish
($handle)
use CompUnit::Util :re-export,:load;
BEGIN steal-globalish(load('SomeModule'));
# This compunit now has everything in SomeModule in it's globalishMerges the GLOBALish from $handle intot he present UNIT::GLOBALish.
This is the least interesting of all the re-exports, and if you've
already done need SomeModule; you won't need it. But it's here for
completeness. The above example should be the same as this anyway:
BEGIN require ::('SomeModule');this routine can only be called at BEGIN time
re-export-everything
($handle)
use CompUnit::Util :re-export;
BEGIN re-export-everything('SomeModule');
# use [this-module]; should now do the same thing as use SomeModule;A convenience method for calling all the other functions under re-export functions with the same argument.
this routine can only be called at BEGIN time
Symbol setting
The following routines manipulate the symbols of the compunit being
compiled. They are probably of most use inside an &EXPORT sub or in
a trait.
They each take a map of symbol names to values and install them in different places.
note: These subs will overwrite existing symbols without warning.
Inserts name/value pairs into the present UNIT::EXPORT under $tag.
set-unit
(Str:D $path,Mu $value)
# like is export, but prefixes the the exported name with 'fun-'
sub trait_mod:<is>(Routine:D $r, :$export-fun!) {
my $exported-name = '&fun-' ~ $r.name;
set-unit("EXPORT::DEFAULT::{$exported-name}",$r);
{};}Inserts the $value at $path in UNIT of the currently compiling
compunit.
this routine can only be called at BEGIN time
set-lexpad
(Str:D $path,Mu $value)
The same as set-unit but inserts the $value into the lexical
scope being compiled.
this routine can only be called at BEGIN time
Symbol Getting
get-unit
(Str:D $path)
use CompUnit::Util :get-symbols;
sub foo is export { };
BEGIN note get-unit('EXPORT::DEFAULT::&foo') === &foo; #-> Trueget-lexpad
(Str:D $path)
The same as get-unit but looks for the symbol in the lexpad
being compiled.
get-lexical
(Str:D $name)
Like get-lexpad but does a full lexical lookup. At the moment it can
only take a single $name with no ::.
Dispatcher Manipulation
These routines help you construct multi dispatchers candidate by
candidate in a procedural manner. Useful when you want to construct a
trait that adds a multi candidate each time it's called. Parameters
marked $multi can be any Routine:D. If you pass a dispatcher it
will just use it as the dispatcher or die if you are trying to push
onto an existing dispatcher.
If you try and push a non-multi/dispatcher onto an empty slot it will not vivify one for you.
push-multi
(Routine:D $target where { .is_dispatcher },Routine:D $candidate)
Adds the $candidate onto $target.
use CompUnit::Util :push-multi;
multi foo('one') { 'one' }
multi foo('two') { 'two' }
&foo.&push-multi(sub ('three') { "win" });
say foo('three') #-> "win"note This is NYI in rakudo. The design docs says that protos should have a
.push method. see S06.
push-unit-multi
(Str:D $path,Routine:D $mutli)
## lib/SillyModule.pm6
use CompUnit::Util :push-multi
# exports the multi under a sub named after its first letter
sub trait_mod:<is>(Routine:D $r,:$one-letter-export!) {
my $exported-name = '&' ~ $r.name.comb[0];
push-unit-multi("EXPORT::DEFAULT::{$exported-name}",$r);
}
multi bar(Str) is one-letter-export { say "bar" }
multi baz(Int) is one-letter-export { say "baz" }
...
use SillyModule;
b("string"); #-> bar
b(1) #-> baz
Takes $multi and pushses it onto a dispatcher located at $path. If
one doesn't exist it will be created. You can pass a proto instead
of a multi but only when $path is empty (ie only the first time). It
will become the dispatcher for any further calls.
this routine can only be called at BEGIN time
push-lexpad-multi
(Str:D $path,Routine:D $mutli)
The same as push-unit-multi but pushes onto a symbol in the lexical
scope currently being compiled.
this routine can only be called at BEGIN time
push-lexical-multi
(Str:D $name,Routine:D $mutli)
The smart version of push-lexpad-multi. If it doesn't find a
dispatcher in the current lexpad it will do a lexical lookup for one
of the same $name. If it finds one it clones it, installs it in the
current lexpad and pushes $multi onto it. Like
get-lexical, it can't take a $name with :: in it.
this routine can only be called at BEGIN time