Consider a simple module like this:
package MyApp::Util::Maths;
use strict;
use warnings;
use constant PI => 3.14159265358979;
use constant EULER => 2.71828182845905;
use base 'Exporter';
our @EXPORT_OK = qw( PI EULER add );
our %EXPORT_TAGS = (
arithmetic => [qw( add )],
numbers => [qw( PI EULER )],
all => @EXPORT_OK,
);
sub add {
my ( $x, $y ) = @_;
return $x + $y;
}
1;
You might use it like:
use MyApp::Util::Maths qw( PI add );
my $pi_plus_one = add( PI, 1 );
Exporter::Almighty is a module designed to reduce boilerplate in your utils-like modules, and increase their functionality.
The initial module can be rewritten as:
package MyApp::Util::Maths;
use Exporter::Almighty -setup => {
const => {
numbers => {
PI => 3.14159265358979,
EULER => 2.71828182845905,
},
},
tag => {
arithmetic => ['add'],
},
};
sub add {
my ( $x, $y ) = @_;
return $x + $y;
}
1;
Exporter::Almighty sets up your exporting automatically (but using Exporter::Tiny instead of Exporter), and calls use strict
and use warnings
on your behalf.
Exporter::Almighty creates your constants for you, so you don’t need to duplicate your list of constants anywhere.
A bonus for your caller is that they can do:
use MyApp::Util::Maths qw( $PI $EULER );
To import read-only $PI
and $EULER
variables instead of traditional constants.
Your caller can also import things lexically:
my $pi_plus_one = do {
use MyApp::Util::Maths -lexical, qw( add $PI );
add( $PI, 1 );
};
# add() and $PI are not defined outside the above block
Your caller can also rename any imported functions:
use MyApp::Util::Maths 'add' => { -as => 'sum_of' };
Exporter::Almighty has integrations with Type::Tiny making it easy to define and export Type::Tiny type constraints as part of your module. For example:
package MyApp::Util::Maths;
use Exporter::Almighty -setup => {
const => { ... },
tag => { ... },
type => { 'Types::Standard' => ['Int', 'Num'] },
class => ['Calc' => { class => 'MyApp::Calculator' }],
};
...;
1;
Now people can import the Int and Num type constraints from your module:
use MyApp::Util::Maths qw( Int );
They can even import a is_Int
function:
use MyApp::Util::Maths qw( is_Int );
You’ve also defined a Calc class type constraint which can be used like this:
has calculator => (
is => 'ro',
isa => Calc,
default => sub { Calc->new },
);
Exporter::Almighty makes defining enum-like data types easy:
package My::Properties {
use Exporter::Almighty -setup => {
enum => { 'Status' => ['alive', 'dead', 'undead'] },
};
}
package Story::Character {
use Moo;
use My::Properties -lexical, '+Status';
use experimental 'signatures';
has status => (
is => 'ro',
isa => Status,
default => STATUS_ALIVE,
);
sub meet ( $self, $other ) {
if ( $self->status eq STATUS_ALIVE
and $other->status eq STATUS_UNDEAD ) {
print "Help!\n";
}
return $self;
}
}
Next time you’re writing a module that needs to export things, consider Exporter::Almighty. It could make things very easy for you, while adding a bunch of useful features for your caller.
Top comments (0)