MAIN subroutine

uzl*_*xxx 6 program-entry-point subroutine perl6 raku

Scenario

Imagine that you have a module X whose functionalities are available to the user through a Command Line Interface. Such module doesn't do much in and of itself but it allows for other people to create plugin-like modules they can hook-up to module X. Ideally, the plugins could be used through X's CLI.

Thus my question:

What do you need to do in order to hook-up whatever functionality a plugin might provide to X's CLI?

This means the plugin would need to provide some structure describing the command, what needs to be called and hopefully a usage message for the plugin. Therefore, when you run X's CLI, the plugin's command and help message shows up in the regular X's CLI's help message.

Example

main.p6:

use Hello;
use Print;

multi MAIN('goodbye') {
    put 'goodbye'
}
Run Code Online (Sandbox Code Playgroud)

lib/Hello.pm6:

unit module Hello;

our %command =  %(
    command => 'hello',
    routine => sub { return "Hello" },
    help    => 'print hello.'
);
Run Code Online (Sandbox Code Playgroud)

lib/Print.pm6:

unit module Print;

our %command =  %(
    command => 'print',
    routine => sub { .print for 1..10 },
    help    => 'print numbers 1 through 10.'
);
Run Code Online (Sandbox Code Playgroud)

该程序main.p6是此方案的简化版本。我想将两者Hello.pm6以及Print.pm6通过它们各自的%command变量提供的信息集成到中的两个新子MAINmain.p6

这可能吗?如果是这样,我将如何实现它?

jjm*_*elo 3

这看起来有点特定于 StackOverflow 问题,但无论如何我都会尝试一下。这里有几个问题。第一个是这样注册命令,以便MAIN可以发出一条消息说“这就是那个”,第二个是实际执行命令。如果两者都可以在编译时知道,那么这个问题可能可以得到解决。但让我们看看实际的代码是如何进行的。我将只完成第一部分,其余部分作为练习。

第一件事是%command需要以某种方式导出。你不能按照现在的方式去做。首先,因为它没有显式导出;如果是这样,您最终会在外部作用域中得到几个具有相同名称的符号。因此,我们需要将其更改为类,以便实际符号对于该类来说是词法的,并且不会污染外部范围。

unit class Hello;

has %.command =  %(
    command => 'hello',
    routine => sub { return "Hello" },
    help    => 'print hello.'
);
Run Code Online (Sandbox Code Playgroud)

(同样适用于Print

只要我们有了这个,剩下的就不那么困难了,只是我们必须使用内省来了解实际存在的内容,作为一个小技巧:

use Hello;
use Print;

my @packages=  MY::.keys.grep( /^^<upper> <lower>/ );
my @commands = do for @packages -> $p {
    my $foo = ::($p).new();
    $foo.command()<command>
};

multi MAIN( $command where * eq any(@commands) ) {
    say "We're doing $command";
}
Run Code Online (Sandbox Code Playgroud)

我们检查符号表,查找以大写字母开头,后跟其他非大写字母的包。碰巧,唯一的包是我们感兴趣的包,但是当然,如​​果您想将其用作插件机制,您应该只使用它们特有的任何模式。然后,我们创建这些新包的实例,并调用命令自动生成的方法来获取命令的名称。这正是我们用来检查我们是否在MAIN子例程中执行正确命令的方法,通过使用where签名来检查我们正在使用的字符串是否确实在已知命令列表中。

由于函数和其余内容也可以从 中获得@packages,因此实际调用它们(或提供附加消息或其他内容)就作为练习。

更新:您可能想查看另一个 StackOveflow 答案,作为模块中签名的替代机制。