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

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.

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.

Related

How can I avoid "not in ecosystem" errors thrown by comma IDE?

I have a test file in my module's t/ directory that does a use on a TestUtils files which has some variables and routines:
use Test;
use TestUtils;
plan 4;
check_cmd_output('No arguments');
check_cmd_output('Successfully ingested', test_md_file);
check_cmd_output('Successfully ingested', test_md_file, 1);
check_cmd_output('Successfully ingested', vimwiki_arg_sim());
It works fine. However, Comma was complaining about TestUtils not being found "in the ecosystem" as well as throwing errors with identifiers from the TestUtils module that it was not loading:
I was able to stop Comma from complaining by:
Exporting the identifiers in the TestUtils file
Fully qualifying the identifiers in my test file with something like: TestUtils::check_cmd_output(...)
Adding TestUtils to the provides hash in the META6.json file.
I'm thinking there is probably a better way. I tried doing stuff like use lib 't' and use lib '.' but that did not help. Thanks.
Comma doesn't handle this situation perfectly; it understands (following the IntelliJ platform naming conventions):
Source roots, which are used as roots for resolving use statements within the project (anything not resolved is assumed to be from the module ecosystem)
Test roots, where test files are found (used for, for example, being able to run tests in an xt directory)
These are, however, mutually exclusive. If the TestUtils module were to be placed in a lib directory inside of t, then that lib directory could be marked as a source root, and the symbols should be resolved.

Perl6 generic code to test if modules load

This is a generic code code in /t to test if .pm6 modules in /lib load.
use lib $*PROGRAM.sibling('../lib');
use Test;
my #dir = dir($*PROGRAM.sibling('../lib'), test => { $_ ~~ /.*pm6/ } );
plan #dir.elems;
sub module( IO $dir ) {
$dir.basename.Str ~~ /(\w+)\.pm6/;
return $0.Str;
}
for #dir.map(&module) -> $module {
use-ok $module, "This module loads: $module";
}
Before going any further (recursively looking at lib sub-folders ), I wonder in this is the right approach.
Thanks!
If you are testing a well-formed distribution then you should be using:
use lib $*PROGRAM.parent(2);
By pointing use lib at the directory containing your META6.json instead of the lib directory you help ensure that the provides entry of the META6.json file is up to date (since files not listed in the META6.json but that do exist inside lib won't be seen).
(I'd even take it one step further and say don't use use lib '...' at all, and instead run your tests using perl6 -I .... For instance -- what if you want to run these tests (for whatever reason) on the installed copy of some distribution?)
With that said you could skip the directory recursion by using the META6 data. One method would be to read the META6.json directly, but a better way of doing so would be to get the module names from the distribution itself:
# file: zef/t/my-test.t
# cwd: zef/
use lib $*PROGRAM.parent(2); # or better: perl6 -I. t/my-test.t
use Test;
my $known-module = CompUnit::DependencySpecification.new(short-name => "Zef");
my $comp-unit = $*REPO.resolve($known-module);
my #module-names = $comp-unit.distribution.meta<provides>.keys;
use-ok($_) for #module-names;
Using #ugexe feedback and META6 distribution, the following code in t/ tests that modules defined in META6.json load.
use META6;
use Test;
my $m = META6.new( file => $*PROGRAM.sibling('../META6.json') );
my #modules = $m<provides>.keys;
plan #modules.elems;
for $m<provides>.keys -> $module {
use-ok $module, "This module loads: $module";
}
This test has been pulled into the META6 distribution.

dynamic-require a module with respect to the current module path in Racket (Or how to find the current module path in Racket)

If I want to optionally require a module at runtime, I can use [dynamic-require'1. This works great if I want to require a package that has been installed, for example:
(dynamic-require 'racket/match 'match)
This will (provided I have racket/match installed), require racket/match and evaluate to the match binding in that library.
However, I run into trouble when I want to require a local, non installed module. Let's say I have some module called eat.rkt, which provides one function: bite:
#lang racket ;; eat.rkt
(provide bite)
(define (bite) "Nom nom")
Now lets say we want to make a lunch.rkt module that requires eat.rkt, and calls the function. Furthermore, lets suppose I put them in the same directory:
#lang racket ;; lunch.rkt
(require "eat.rkt")
(bite) ; => Nom Nom
This is fine because I used static require, but this falls apart when I want to do a dynamic-require:
#lang racket ;; lunch2.rkt
(define bite (dynamic-require "eat.rkt" 'bite)
(bite) ; => Nom Nom
While this appears to be fine, it turns out that the module required by dynamic-require is NOT required based on the module's path, but on current-directory. So, if I run the program in the directory the module is defined, that's fine, but if I'm in another directory, everything breaks:
$ racket lunch2.rkt
"Nom Nom"
$ cd snacks/
$ racket ../lunch2.rkt
; default-load-handler: cannot open module file
Obviously I could just change the current-directory to this module's directory if I know where it is. But if I don't know this module's path, is there any way to get it? Or, more directly, is it possible to dynamic-require a module relative to the requiring's module path?
The define-runtime-path form defines a path that will be available at runtime and is independent of the current-directory. Use it to define the path to the module you want to require dynamically, for example:
#lang racket
(require racket/runtime-path)
(define-runtime-path eat "eat.rkt")
(dynamic-require eat 'bite)
The easiest way to dynamic-require a module relative to the current module path (which is to say the path where the module is saved), is to get that module path and append it your relative module.
You can do this with #%variable-reference and variable-reference->module-path-index. (You may also want to use variable-reference->resolved-module-path for other situations, but we will not do it here.) Composing these two functions gives us a module-path-index? to the module being defined. (Or in general, the module that the #%variable-reference came from.)
So, we can a variable like:
(define here (variable-reference->module-path-index (#%variable-reference)))
Now all that is left is to compose this here path with the relative path to the module we want to require. We are looking for the module path analogy of build-path, if you will.
It turns out that the function we are looking for is: module-path-index-join, which takes a base path and a relative path and appends them together. The result will look something like:
(module-path-index-join "eat.rkt" here)
(Yes, it is backwards of what you would expect from build-path, but the base path comes second for this function.)
The resulting module, lunch3.rkt looks like:
#lang racket
(define here (variable-reference->module-path-index (#%variable-reference)))
(define bite (dynamic-require (module-path-index-join "eat.rkt" here) 'bite))
And now lunch3.rkt will require eat.rkt relative to where its defined, not based on the current-directory:
$ racket lunch3.rkt
"Nom Nom"
$ cd snacks/
$ racket ../lunch3.rkt
"Nom Nom"
Thank you to Matthew Flatt for helping with this answer.

How to organize Lua module path and write "require" calls without losing flexibility?

Say I have a project, whose folder structure looks like below:
| main.lua
|
|---<model> // this is a folder
| |a.lua
| |b.lua
|
|---<view>
|a.lua
|b.lua
model/a.lua requries model/b.lua: require "b"
view/a.lua requries view/b.lua: require "b"
main.lua requries files in model and view.
Now I have problem to get these modules loaded correctly. I know I can fix it by changing the require calls to:
model/a.lua: require "model.b"
view/a.lua: require "view.b"
But if I do that, I have to modify these files every time when I change the folder structure.
So my questions are:
How to fix the module path issue without hard code paths in module files?
Why Lua doesn't use the module search rule of Node.js, which looks easier?
When you require a module, the string parameter from require gets passed into the module which you can access using the variable-argument syntax .... You can use this to include other dependent modules which reside in the same path as the current module being requireed without making it dependent on a fixed hard-coded module name.
For your example, instead of doing:
-- model/a.lua
require "model.b"
and
-- view/a.lua
require "view.b"
You can do:
-- model/a.lua
local thispath = select('1', ...):match(".+%.") or ""
require(thispath.."b")
and
-- view/a.lua
local thispath = select('1', ...):match(".+%.") or ""
require(thispath.."b")
Now if you change directory structure, eg. move view to something like control/subcontrol/foobar, then control/subcontrol/foobar/a.lua (formerly view/a.lua) will now try to require control/subcontrol/foobar/b.lua instead and "do the right thing".
Of course main.lua will still need to fully qualify the paths since you need some way to disambiguate between model/a.lua and view/a.lua.
How to fix the module path issue without hard code paths in module files?
I don't have any better cross-platform solution, maybe you should plan the folder structure early on.
Why Lua doesn't use the module search rule of Node.js, which looks easier?
Because Lua tries its best to rely only on ANSI C, which is really successful. And in ANSI C, there's no such concept of directories.
There are a couple approaches you can use.
You can add relative paths to package.path as in this SO answer. In your case you'd want to add paths in main.lua that correspond to the various ways you might access the files. This keeps all the changes required when changing your directory structure local to one file.
You can add absolute paths to package.pathusing debug.getinfo -- this may be a little easier since you don't need to account for all the relative accesses, but you still need to do this in main.lua when changing your directory structure, and you need to do string manipulation on the value returned by debug.getinfo to strip the module name and add the subdirectory names.
> lunit = require "lunit"
> info = debug.getinfo(lunit.run, "S")
> =info.source
#/usr/local/share/lua/5.2/lunit.lua
> =info.short_src
/usr/local/share/lua/5.2/lunit.lua
The solution is to add the folder of main.lua (project root) to package.path in main.lua.
A naive way to support folders of 1 level deep:
-- main.lua
package.path = package.path .. ";../?.lua"
Note for requires in (project root) will look up files outside of project root, which is not desirable.
A better way of to use some library (e.g.: paths, penlight) to resolve the absolute path and add it instead:
-- main.lua
local projectRoot = lib.abspath(".")
package.path = package.path .. ";" .. projectRoot .. "/?.lua"
Then in you source use the folder name to scope the files:
-- model/a.lua
require "model.b"
-- you can even do this
require "view.b"
and
-- view/a.lua
require "view.b"

Get script name in OCaml?

Does OCaml have a way to get the current file/module/script name? Something like:
C/C++'s argv[0]
Python's sys.argv[0]
Perl/Ruby's $0
Erlang's ?FILE
C#'s ProgramName.Environment.CommandLine
Factor's scriptname/script
Go's os.Args[0]
Haskell's System/getProgName
Java's System.getProperty("sun.java.command").split(" ")[0]
Node.js's __filename
etc.
I don't know anything about OCaml but some googling turned up
Sys.argv.(0)
See http://caml.inria.fr/pub/docs/manual-ocaml/manual003.html#toc12
I presume you are scripting in OCaml. Then Sys.argv.(0) is the easiest way to get the script name. Sys module also provides Sys.executable_name, but its semantics is slightly different:
let _ = prerr_endline Sys.executable_name; Array.iter prerr_endline Sys.argv;;
If I run the above line, putting the line in test.ml, by ocaml test.ml hello world, I have:
/usr/local/bin/ocaml - executable_name
test.ml - argv.(0)
hello - argv.(1)
world - argv.(2)
So OCaml toplevel does something fancy against argv for you.
In general, obtaining the current module name in OCaml is not easy, from several reasons:
ML modules are so flexible that they can be aliased, included into other modules, and applied to module functors.
OCaml does not embed the module name into its object file.
One probably possible workaround is to add a variable for the module name by yourself, like:
let ml_source_name = "foobar.ml"
This definition can be probably auto inserted by some pre-processing. However, I am not sure CamlP4 can have the file name of the currently processing source file.
If your main purpose is simple scripting, then of course this pre-processing is too complicated, I am afraid.
let _ =
let program = Sys.argv.(0) in
print_endline ("Program: " ^ program)
And posted to RosettaCode.
In OCaml >= 4.02.0, you can also use __FILE__ to get the filename of the current file, which is similar to Node's __filename and not the same as Sys.argv.(0).