Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I access a module's symbol table dynamically at runtime in Raku?

Tags:

module

raku

I want to be able to pass my script the path to a .rakumod file, say <blah>/Mod.rakumod, and be able to access the symbol table as a hash as if I'd used the module instead:

The module:

$ cat Mod.rakumod
unit module Mod;
sub bag        is export             { ... }
use lib <dir-containing-Mod>
use Mod;
say Mod::EXPORT::.keys

works, as expected, returning (ALL DEFAULT).

On the other hand:

use lib <dir-containing-Mod>
require Mod;
say Mod::EXPORT::.keys

fails with

Could not find symbol '&EXPORT' in 'Mod'
  in block <unit> at <blah>

This is despite the fact that even with require, say Mod::.keys does see EXPORT:

use lib <dir-containing-Mod>
require Mod;
say Mod::.keys
---
(EXPORT Mod)

I need to use require to make this dynamic, as I don't know which module I'll want.


I can actually think of one thing to do, but it is absolutely disgusting:

  • save my module name into a variable $mod
  • have my script write another script using that module:
my $mod = <whatever>
my $cd = qq:to/END/;
use v6;
use lib qq\|\$\*CWD\|;
use $mod;

say {$mod}::EXPORT::ALL::.keys;
END

'aux.p6'.IO.spurt($cd);
  • and then have the initial script call the auxiliary one:
shell("raku aux.p6")

What worked, per raiph's answer (which I've accepted and am here paraphrasing):

  • pass the path to the module and save it as $path;
  • use lib the relevant directory:
my $dir = $path.IO.dirname;
use lib $dir;
  • extract the plain file name:
my $modFile = S/(.*)\..*/$0/ with $path.IO.basename;
  • finally, require that and pull the .WHO trick from raiph's answer, slightly adapted:
require ::($modFile);
say ::("{$modFile}::EXPORT::ALL").WHO.keys;

Run with <script> <path> that returned (&bag) all right.


Actually, the above doesn't quite work: use lib $dir will fail saying $dir is empty, because use lib isn't dynamic.

So instead I am now resorting to the unappealing solution of

  • copying the module file to a temporary directory ./TMP
  • having called use './TMP';
  • and then removing that directory when done.
like image 352
grobber Avatar asked Apr 11 '21 01:04

grobber


1 Answers

TL;DR Use dynamic symbol lookup to get the symbol of the package whose symbols you want at run-time; then .WHO to get its stash; then .keys to get that stash's symbols.

For example:

use lib '.';
require Mod;
say ::Mod::EXPORT::('ALL').WHO.keys; # (&bag)

I'm going to make breakfast. I'll elaborate later.

like image 166
raiph Avatar answered Nov 11 '22 03:11

raiph