perlmoose

How can I export a subroutine from a Moose package?


How can I export a normal, non-OO subroutine from a Moose package? In a regular package, I'd do it with Exporter, @ISA and @EXPORT.


Solution

  • Moose is for building classes and roles. While you technically can also export functions, it's not necessarily the best idea.

    Here's an example Moose class which also exports a function.

    MyApp/Widget.pm

    use v5.26;
    use warnings;
    
    package MyApp::Widget;
    
    use Exporter qw( import );
    our @EXPORT_OK = qw( is_widget );
    
    use Moose;
    use namespace::autoclean -except => 'import';
    
    has name => ( is => 'ro', isa => 'Str', required => 1 );
    
    sub is_widget {
        my $object = shift;
        blessed( $object ) and $object->isa( __PACKAGE__ );
    }
    
    __PACKAGE__->meta->make_immutable;
    

    Here's how you might use it:

    use v5.26;
    use warnings;
    use MyApp::Widget qw( is_widget );
    
    my $w = 'MyApp::Widget'->new( name => 'Foo' );
    say is_widget( $w );
    say $w->is_widget;
    

    Note that even though is_widget was intended an exportable function, it can also be called as a method! In this case, that's a feature rather than a bug, but often that will be an inconvenience.

    A better idea might be to create two separate packages: one for your class and one for your exportable functions.

    MyApp/Widget.pm

    use v5.26;
    use warnings;
    
    package MyApp::Widget;
    
    use Moose;
    use namespace::autoclean;
    
    has name => ( is => 'ro', isa => 'Str', required => 1 );
    
    __PACKAGE__->meta->make_immutable;
    

    MyApp/Util.pm

    use v5.26;
    use warnings;
    
    package MyApp::Util;
    
    use Exporter qw( import );
    our @EXPORT_OK = qw( is_widget );
    
    use Scalar::Util qw( blessed );
    
    sub is_widget {
        my $object = shift;
        blessed( $object ) and $object->isa( 'MyApp::Widget' );
    }
    
    1;
    

    And you'd call use your packages like this:

    use v5.26;
    use warnings;
    use MyApp::Widget;
    use MyApp::Util qw( is_widget );
    
    my $w = 'MyApp::Widget'->new( name => 'Foo' );
    say is_widget( $w );
    

    Because the Moose class and the Exporter are now cleanly separated, you can no longer call $w->is_widget — it's entirely a function and no longer a method.