Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using modules to load a group of related functions

Tags:

raku

I want to use Raku Modules to group some functions, I often use. Because these functions are all loosely coupled, I don't like to add them in a class.

I like the idea of use, where you can select, which functions should be imported, but I don't like it, that the functions, which are imported are then stored in the global namespace.

For example if I have a file my_util.pm6:

#content of my_util.pm6
unit module my_util;
our sub greet($who) is export(:greet) {
    say $who;
}
sub greet2($who) is export(:greet2) {
    say $who;
}
sub greet3($who) is export(:greet3) {
    say $who;
}

and a file test.p6:

#!/usr/bin/perl6
#content of test.p6
use v6.c;
use lib '.';
use my_util :greet2;

greet("Bob");    #should not work (because no namespace given) and also doesn't work
greet2("Bob");   #should not work (because no namespace given) but actually works
greet3("Bob");   #should not work (because no namespace given) and also doesn't work
my_util::greet("Alice");     #works, but should not work (because it is not imported)
my_util::greet2("Alice");    #should work, but doesn't work
my_util::greet3("Alice");    #should not work (because it is not imported) and also doesn't work

I would like to call all functions via my_util::greet() and not via greet() only.

The function greet() defined in my_util.pm6 comes very close to my requirements, but because it is defined as our, it is always imported. What I like is the possibility, to select which functions should be imported and it should be possible to leave it in the namespace defined by the module (i.e. it doesn't pollute the global namespace)

Does anyone know, how I can achieve this?

like image 695
byteunit Avatar asked Aug 19 '17 09:08

byteunit


1 Answers

To clear up some potential confusion...

Lexical scopes and package symbol tables are different things.

  1. my adds a symbol to the current lexical scope.

  2. our adds a symbol to the current lexical scope, and to the public symbol table of the current package.

  3. use copies the requested symbols into the current lexical scope.
    That's called "importing".

  4. The :: separator does a package lookup – i.e. foo::greet looks up the symbol greet in the public symbol table of package foo.
    This doesn't involve any "importing".

As for what you want to achieve...

The public symbol table of a package is the same no matter where it is referenced from... There is no mechanism for making individual symbols in it visible from different scopes.

You could make the colons part of the actual names of the subroutines...

sub foo::greet($who) is export(:greet) { say "Hello, $who!" }
# This subroutine is now literally called "foo::greet".

...but then you can't call it in the normal way anymore (because the parser would interpret that as rule 4 above), so you would have to use the clunky "indirect lexical lookup" syntax, which is obviously not what you want:

foo::greet "Sam";          # Could not find symbol '&greet'
::<&foo::greet>( "Sam" );  # Hello, Sam!

So, your best bet would be to either...

  • Declare the subroutines with our, and live with the fact that all of them can be accessed from all scopes that use the module.
    Or:
  • Add the common prefix directly to the subroutine names, but using an unproblematic separator (such as the dash), and then import them normally:
unit module foo;
sub foo-greet($who)  is export(:greet)  { ... }
sub foo-greet2($who) is export(:greet2) { ... }
sub foo-greet3($who) is export(:greet3) { ... }
like image 178
smls Avatar answered Nov 17 '22 01:11

smls