How do I make a modulino in Perl6?
Asked Answered
G

1

7

I want to make a modulino (a file that can run as either a module or a script) in Perl6.

The following code "processes" file names from the command line:

sub MAIN ( *@filenames )
{
    for @filenames -> $filename
    {
        say "Processing $filename";
    }
}

Saving this as main.pm6 I can run it and it works:

perl6 main.pm6 hello.txt world.txt
Processing 'hello.txt'
Processing 'world.txt'

So, I want this to be a module so that I can add functions and make testing it easier. However, when I add a module declaration to it, it no longer outputs anything:

module main;
sub MAIN ( *@filenames )
{
    for @filenames -> $filename
    {
        say "Processing '$filename'";
    }
}

Which results in nothing output:

perl6 main.pm6 hello.txt world.txt

So, how can I build a modulino in Perl6?

I'm using Perl6 running on MoarVM from the January 2015 release of Rakudo Star.

UPDATE:

When I try wrapping the module in braces:

module main
{
    sub process (@filenames) is export
    {
        for @filenames -> $filename
        {
            say "Processing '$filename'";
        }
    }
};

sub MAIN ( *@filenames )
{
    process(@filenames)
}

I also get errors:

===SORRY!=== Error while compiling main.pm6
Undeclared routine:
    process used at line 14. Did you mean 'proceed'?
Gynandry answered 26/3, 2015 at 14:39 Comment(3)
it should be enough to put it outside the module, ie module main { ... }; sub MAIN { ... }Lemniscate
@Lemniscate It didn't work, but thanks for the suggestion. Is this a bug?Gynandry
Not a bug, but a consequence of lexical scoping. There are several ways to structure your code: Declare process outside the module, make it our-scoped and call is as main::process or export it and add an import statement to the body of your sub MAINLemniscate
L
8

The MAIN sub needs to be declared outside the module, but it still must be able to see process.

There are multiple ways to achieve this, eg by not declaring a module at all

sub process(@filenames) {
    for @filenames -> $filename {
        say "Processing '$filename'";
    }
}

sub MAIN(*@filenames) {
    process(@filenames);
}

by making process our-scoped and calling it by its longname

module main {
    our sub process(@filenames) {
        for @filenames -> $filename {
            say "Processing '$filename'";
        }
    }
}

sub MAIN(*@filenames) {
    main::process(@filenames);
}

or by exporting process and importing it in the body of MAIN

module main {
    sub process(@filenames) is export {
        for @filenames -> $filename {
            say "Processing '$filename'";
        }
    }
}

sub MAIN(*@filenames) {
    import main;
    process(@filenames);
}

In my opinion the most appropriate option is to add MAIN to the module and import it into the script's mainline. This way, everything declared within the module is visible within MAIN without having to explicitly export everything:

module main {
    sub process(@filenames) {
        for @filenames -> $filename {
            say "Processing '$filename'";
        }
    }

    sub MAIN(*@filenames) is export(:MAIN) {
        process(@filenames);
    }
}

import main :MAIN;

Note that this does not export MAIN by default, ie users of your module will only get it if they provide the :MAIN tag.

Lemniscate answered 26/3, 2015 at 16:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.