Modules

How to create, use and distribute Perl 6 modules

Creating and Using Modules

A module is usually a source file or set of source files [1] that expose Perl 6 constructs. These are typically packages (classes, roles, grammars), subroutines, and sometimes variables. In Perl 6 module can also refer to a type of package declared with the module keyword (see example below) but here we mostly mean "module" as a set of source files in a namespace.

Basic Structure

Module distributions (in the set of related source files sense) in Perl 6 have the same structure as any distribution in the Perl family of languages: there is a main project directory containing a README and a LICENSE file, a lib directory for the source files, which may be individually referred to as modules and/or may themselves define modules with the module keyword [2], a t directory for tests, and possibly a bin directory for executable programs and scripts.

Source files generally use the standard .pm extension, and scripts or executables use .pl. However, if you wish to highlight that the file is written in Perl 6 you can use the .pm6 extension for modules, and .p6 or .pl6 extension for scripts. Test files still use the normal .t extension. For windows compatibility, .p6 is the preferred extension for cross-platform scripts.

Loading and Basic Importing

Loading a module makes the packages in the same namespace declared within available in the file scope of the loader. Importing from a module makes the symbols exported available in the lexical scope of the importing statement.

need

need loads a compunit at compile time.

need MyModule;

Any packages in the namespace defined within will also be available.

# MyModule.pm 
unit module MyModule;
 
class MyModule::Class { ... }

MyModule::Class will be defined when MyModule is loaded.

use

use loads and then imports from a compunit at compile time.

use MyModule;

It is equivalent to:

need MyModule;
import MyModule;

See also Selective Importing to restrict what you import.

require

require loads a compunit and imports definite symbols at runtime.

say "loading MyModule";
require MyModule;

The compunit name can be in a runtime variable if you put it inside an indirect lookup.

my $name = 'MyModule';
require ::($name);

The symbols provided by the loaded module will not be imported into the current scope. You may use dynamic lookup or dynamic subsets to use them by providing the fully qualified name of a symbol.

To import symbols you must define them at compile time. NOTE: require is lexically scoped:

sub do-something {
   require MyModule <&something>;
   say ::('MyModule'); # MyModule symbol exists here 
   something() # &something will be defined here 
}
say ::('MyModule'); # This will NOT contain the MyModule symbol 
do-something();
# &something will not be defined here 

If MyModule doesn't export &something then require will fail.

A require with compile-time symbol will install a placeholder package that will be updated to the loaded module, class, or package. Note that the placeholder will be kept, even if require failed to load the module. This means that checking if a module loaded like this is wrong:

# *** WRONG: *** 
try require Foo;
if ::('Foo'~~ Failure { say "Failed to load Foo!"}
# *** WRONG: *** 

As the compile-time installed package causes ::('Foo') to never be a Failure. The correct way is:

# Use return value to test whether loading succeeded: 
(try require Foo=== Nil and say "Failed to load Foo!";
 
# Or use a run-time symbol lookup with require, to avoid compile-time 
# package installation: 
try require ::('Foo');
if ::('Foo'~~ Failure {
    say "Failed to load Foo!";
}

Exporting and Selective Importing

is export

Packages, subroutines, variables, constants and enums are exported by marking them with the is export trait (also note the tags available for indicating authors and versions).

unit module MyModule:ver<1.0.3>:auth<John Hancock (jhancock@example.com)>;
our $var is export = 3;
sub foo is export { ... };
constant FOO is export = "foobar";
enum FooBar is export <one two three>;
 
# Packages like classes can be exported too 
class MyClass is export {};
 
# If a subpackage is in the namespace of the current package 
# it doesn't need to be explicitly exported 
class MyModule::MyClass {};

As with all traits, if applied to a routine, "is export" should appear after any argument list.

sub foo(Str $stringis export { ... }

You can pass named parameters to is export to group symbols for exporting so that the importer can pick and choose. There are three predefined tags: ALL, DEFAULT and MANDATORY.

# lib/MyModule.pm 
unit module MyModule;
sub bag        is export             { ... }
sub pants      is export(:MANDATORY{ ... } # objects with tag ':MANDATORY' are always exported 
sub sunglasses is export(:day)       { ... }
sub torch      is export(:night)     { ... }
sub underpants is export(:ALL)       { ... }
# main.pl 
use lib 'lib';
use MyModule;          # bag, pants 
use MyModule :DEFAULT# the same 
use MyModule :day;     # pants, sunglasses 
use MyModule :night;   # pants, torch 
use MyModule :ALL;     # bag, pants, sunglasses, torch, underpants 

Note there currently is no way for the user to import a single object if the module author hasn't made provision for that, and it is not an easy task at the moment (see RT #127305). One way the author can provide such access is to give each export trait its own unique tag. (And the tag can be the object name!) Then the user can either (1) import all objects:

use Foo :ALL;

or (2) import one or more objects selectively:

use Foo :bar:s5;

Notes:

1. The :MANDATORY tag on an exported sub ensures it will be exported no matter whether the using program adds any tag or not.

2. All exported subs without an explicit tag are implicitly :DEFAULT.

3. The space after the module name and before the tag is mandatory.

4. Multiple import tags may be used (separated by commas). For example:

# main.pl 
use lib 'lib';
use MyModule :day:night# pants, sunglasses, torch 

5. Multiple tags may be used in the export trait, but they must all be separated by either commas, or whitespace, but not both.

sub foo() is export(:foo :s2 :net{}
sub bar() is export(:bar:s3:some{}

UNIT::EXPORT::*

Beneath the surface, is export is adding the symbols to a UNIT scoped package in the EXPORT namespace. For example, is export(:FOO) will add the target to the UNIT::EXPORT::FOO package. This is what Perl 6 is really using to decide what to import.

unit module MyModule;
 
sub foo is export { ... }
sub bar is export(:other{ ... }

Is the same as:

unit module MyModule;
 
my package EXPORT::DEFAULT {
    our sub foo { ... }
}
 
my package EXPORT::other {
    our sub bar { ... }
}

For most purposes is export is sufficient but the EXPORT packages are useful when you want to produce the exported symbols dynamically. For example:

# lib/MyModule.pm 
unit module MyModule;
 
my package EXPORT::DEFAULT {
   for <zero one two three four>.kv -> $number$name {
      for <sqrt log> -> $func {
         OUR::{'&' ~ $func ~ '-of-' ~ $name } := sub { $number."$func"() };
      }
   }
}
 
# main.pl 
use MyModule;
say sqrt-of-four# OUTPUT: «2␤» 
say log-of-zero;  # OUTPUT: «-Inf␤» 

EXPORT

You can export arbitrary symbols with an EXPORT sub. EXPORT must return a Map, where the keys are the symbol names and the values are the desired values. The names should include the sigil (if any) for the associated type.

# lib/MyModule.pm 
 
class MyModule::Class { ... }
 
sub EXPORT {
    {
     '$var'   => 'one',
     '@array' => <one two three>,
     '%hash'  => { one => 'two'three => 'four' },
     '&doit'   => sub { ... },
     'ShortName' => MyModule::Class
    }
}
# main.pl 
use lib 'lib';
use MyModule;
say $var;
say @array;
say %hash;
doit();
say ShortName.new# OUTPUT: «MyModule::Class.new␤» 

Note, EXPORT can't be declared inside a package because it is part of the compunit rather than the package.

Whereas UNIT::EXPORT packages deal with the named parameters passed to use, the EXPORT sub handles positional parameters. If you pass positional parameters to use they will be passed to EXPORT. If a positional is passed the module no longer exports default symbols. You may still import them explicitly by passing :DEFAULT to use along with your positional parameters.

# lib/MyModule 
 
class MyModule::Class {}
 
sub EXPORT($short_name?{
    {
      do $short_name => MyModule::Class if $short_name
    }
}
 
sub always is export(:MANDATORY{ say "works" }
 
#import with :ALL or :DEFAULT to get 
sub shy is export { say "you found me!" }
# main.pl 
use lib 'lib';
use MyModule 'foo';
say foo.new(); # MyModule::Class.new 
always();      # OK   - is imported 
shy();         # FAIL - won't be imported 

You can combine `EXPORT` with type captures for interesting effect. This example creates a `?` postfix which will only work on Cools.

# lib/MakeQuestionable.pm 
sub EXPORT(::Questionable{
    my multi postfix:<?>(Questionable $_{ .so };
    {
      '&postfix:<?>' => &postfix:<?>,
    }
}
use MakeQuestionable Cool;
say 0?1?{}?{ => "b" }?# OUTPUT: «False True False True␤» 

Introspection

To list exported symbols of a module first query the export tags supported by the module.

use URI::Escape;
dd URI::Escape::EXPORT::.keys;
# OUTPUT: «("DEFAULT", "ALL").Seq» 

Then use the tag you like and pick the symbol by its name.

dd URI::Escape::EXPORT::DEFAULT::.keys;
# OUTPUT: «("\&uri-escape", "\&uri_escape", "\&uri-unescape", "\&uri_unescape").Seq» 
my &escape-uri = URI::Escape::EXPORT::DEFAULT::<&uri_escape>;

Finding Modules

A user may have a collection of modules not found in the normal ecosystem, maintained by a module or package manager, but needed regularly. Instead of using the use lib pragma one can use the PERL6LIB environment variable to point to module locations. For example:

export PERL6LIB=/path/to/my-modules,/path/to/more/modules

Note that the comma (',') is used as the directory separator (instead of the colon (':') as with Perl 5 for PERL5LIB or PERLLIB).

The include path will be searched recursively for any modules when Rakudo is started. Directories that start with a dot are ignored and symlinks are followed.

Distributing Modules

If you've written a Perl 6 module and would like to share it with the community, we'd be delighted to have it listed in the Perl 6 modules directory. :)

For now, the process requires that you use git for your module's version control.

The instructions herein assume that you have a GitHub account so that your module can be shared from its GitHub repository, however another provider such as GitLab should work as long as it works in a similar way.

To share your module, do the following:

That's it! Thanks for contributing to the Perl 6 community!

If you'd like to try out installing your module, use the zef module installer tool which is included with Rakudo Star Perl 6:

zef install Vortex::TotalPerspective

This will download your module to its own working directory (~/.zef), build it there, and install the module into your local Perl 6 installation directory.

To use Vortex::TotalPerspective from your scripts, just write use Vortex::TotalPerspective, and your Perl 6 implementation will know where to look for the module file(s).

Modules and Tools Related to Module Authoring

You can find a list of modules and tools that aim to improve the experience of writing/test modules at Modules Extra

The Future of the Ecosystem

https://modules.perl6.org and github-based infrastructure is temporary. The plan is to establish something similar to Perl 5's PAUSE/CPAN/MetaCPAN infrastructure. Volunteers needed!

The rough plan is:

1. fix EVAL precomp bug (nine)
2. get Repository API straight
3. get zef up to speed
4. continue with the metacpan fork for perl6 (jdv79)

The repository with jdv's fork can be found at https://github.com/jdv/metacpan-web

You can also already upload your Perl 6 modules to Perl 5's PAUSE, selecting `Perl6` directory during the upload. That will ensure your module is indexed in Perl 6's space and not Perl 5's.

Contact Information

To discuss module development in general, or if your module would fill a need in the ecosystem, naming, etc., you can use the perl6 on irc.freenode.net IRC channel.

To discuss toolchain specific questions, you can use the perl6-toolchain on irc.freenode.net IRC channel. A repository to discuss tooling issues is also available at https://github.com/perl6/toolchain-bikeshed.