问题
Here's a short Perl 6 program that declare a MAIN subroutine. I should only see output if I execute the program directly:
$ cat main.pm6
sub MAIN { say "Called as a program!" }
And I see output when I execute the program directly:
$ perl6 main.pm6
Called as a program!
If I load it as a module, I see no output:
$ perl6 -I. -Mmain -e "say 'Hey'"
Hey
Same if I use
it from inside the program, I see no output:
$ perl6 -I. -e 'use main'
But, if I use require
, I get output:
$ perl6 -I. -e 'require <main.pm6>'
Called as a program!
Synopsis 06 literally says the compilation unit was directly invoked rather than by being required. Is there something else going on because require
works at runtime (although S06 doesn't exclude that)?
I get the same behaviour with Rakudo Star 2016.07 and 2016.10.
回答1:
First, let's take a look at how require
is supposed to behave:
According to the (non-authorative) design documents,
Alternately, a filename may be mentioned directly, which installs a package that is effectively anonymous to the current lexical scope, and may only be accessed by whatever global names the module installs:
and
Only explicitly mentioned names may be so imported. In order to protect the run-time sanctity of the lexical pad, it may not be modified by
require
.
In combination with S06's
This call is performed if and only if:
a) the compilation unit was directly invoked rather than by being required by another compilation unit [...]
it is my understanding that a sub MAIN
not explicitly imported into the mainline lexical scope should not be run.
Sadly, the user documentation is quiet on the case of runtime importation via file name, and a quick glance at the (authorative) test suite (in particular S11-modules/require.t) did not yield an answer either, though I just might have missed it.
Now, let's take a look at how Rakudo behaves:
As expected, runtime importation via static or dynamic module name
require main;
or
require ::('main');
will not run MAIN
unless it is both declared is export
and explicitly imported, ie
require main <&MAIN>;
and
require ::('main') <&MAIN>;
respectively.
Importation via file name however
require 'main.pm6';
will immediately run MAIN
.
In fact, if you do a proper import via
require 'main.pm6' <&MAIN>;
the sub will be executed twice: Once when loading the compilation unit, and a second time when the runtime does its job looking and running any MAIN
sub in the mainline scope.
Rakudo apparently treats a require
with file name argument more or less like EVALFILE
and executes its mainline, including any sub MAIN
it encounters.
That's not what I would have expected and possibly just a bug.
回答2:
The Synopsis documents are not a source of truth, and are generally outdated in most places.
The way you use/require can also change the behavior of how a module is loaded. -M does not take the 'use xxx' code path, and using a file name instead of a module name also changes things. Note that zef 'use'-es a module with MAIN and has output show up https://github.com/ugexe/zef/blob/master/bin/zef
来源:https://stackoverflow.com/questions/40778852/should-perl-6-run-main-if-the-file-is-required