Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I make a modulino in Perl6?

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'?
like image 1000
Christopher Bottoms Avatar asked Mar 26 '15 14:03

Christopher Bottoms


1 Answers

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.

like image 109
Christoph Avatar answered Nov 20 '22 19:11

Christoph