Search code examples
raku

How can I dynamically export classes in raku?


I am updating a raku module (Physics::Measure)

My old code looks like this:

unit module Physics::Measure ...

class Length             is Measure is export {}
class Mass               is Measure is export {}
class Current            is Measure is export {}
...

On the client side, we go:

use Physics::Measure;

say my Length $b = 15m;    #15m

To handle localisation, I would like to declare and export these classes programatically, something like:

my @names = <Length Mass Current>;

for @names -> $name {
    class ::($name) is Measure is export {}
}

This gives the error Name ::($name) is not compile-time known, and can not serve as a package name.

Okaay, so maybe (from the docs) I can go something like:

my package EXPORT::DEFAULT {
    for @names -> $name {
        OUR::($name) := $name;
    }
}

-or maybe, with a for loop tbd-

sub EXPORT {
    Map.new:
        ($name => Length is Measure)
}

Please can someone provide a steer on the best way to (i) dynamically declare the classes and (ii) then to export them???


Solution

  • The secret is really in the notion the names of the classes shouldn't be dynamic, but the names with which they are exported should be dynamic:

    # file lib/Physics/Measure.rakumod
    class Length  {}
    class Mass    {}
    class Current {}
    
    sub EXPORT(*@names) {
        Map.new( @names.map: -> $from, $to { $to => ::($from) } )
    }
    

    This would allow you to specify orginal name -> wanted name pairs as such:

    $ raku -Ilib -e 'use Physics::Measure <Length Foo Current Bar>; dd Foo, Bar'
    Length
    Current
    

    This is the basic mechanism for handling any non-named arguments in a use statement. Using named arguments is currently handled by the default handler, and that sadly currently not possible. A much more natural interface would have been :Length<Foo>, :Current<Bar>.