DEV Community

Toby Inkster
Toby Inkster

Posted on • Originally published at toby.ink on

1

Introducing Exporter::Almighty

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;
Enter fullscreen mode Exit fullscreen mode

You might use it like:

  use MyApp::Util::Maths qw( PI add );

  my $pi_plus_one = add( PI, 1 );
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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 );
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Your caller can also rename any imported functions:

  use MyApp::Util::Maths 'add' => { -as => 'sum_of' };
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

Now people can import the Int and Num type constraints from your module:

  use MyApp::Util::Maths qw( Int );
Enter fullscreen mode Exit fullscreen mode

They can even import a is_Int function:

  use MyApp::Util::Maths qw( is_Int );
Enter fullscreen mode Exit fullscreen mode

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 },
  );
Enter fullscreen mode Exit fullscreen mode

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;
    }
  }
Enter fullscreen mode Exit fullscreen mode

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.

Image of Timescale

🚀 pgai Vectorizer: SQLAlchemy and LiteLLM Make Vector Search Simple

We built pgai Vectorizer to simplify embedding management for AI applications—without needing a separate database or complex infrastructure. Since launch, developers have created over 3,000 vectorizers on Timescale Cloud, with many more self-hosted.

Read full post →

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more