1nickt has asked for the wisdom of the Perl Monks concerning the following question:

Hi all,

I have a Moo role in which I declare some constants with:

package MyRole; use Moo::Role; use constant INDEX_FOO => 42; ...

The constants are not available in the consuming class:

package MyClass; use Method::Signatures; use Moo; use namespace::clean; with 'MyRole'; my $aref = get_some_stuff(); my $thing = $aref->[ INDEX_FOO ]; ...

The error:

Bareword "INDEX_FOO" not allowed while "strict subs" in use at ...

Is this expected behaviour? And, can I export my constants from a role to its consumer somehow?

Thank you, kind monks.


The way forward always starts with a minimal test.

Replies are listed 'Best First'.
Re: Exporting constants from a Moo role
by tobyink (Canon) on Aug 02, 2018 at 13:03 UTC

    Firstly use constant doesn't define its constants with the sub name in the correct package. Moo::Role knows they were compiled into the constant:: package, not into your role, so doesn't copy them to your class.

    Secondly, with is operating at run-time, not compile time.

    You can solve these by using PerlX::Define to define your constants, and wrapping with with BEGIN { ... }.

    use v5.16; package MyRole { use Moo::Role; use PerlX::Define; define BLAH = 42; } package MyClass { use Moo; BEGIN { with 'MyRole' }; sub do_stuff { return BLAH } } say MyClass->new->do_stuff;

    Or (same idea) just manually do the constant sub yourself:

    use v5.16; package MyRole { use Moo::Role; sub BLAH () { 42 } } package MyClass { use Moo; BEGIN { with 'MyRole' }; sub do_stuff { return BLAH } } say MyClass->new->do_stuff;

      Thanks Toby! So much information I didn't know, and a choice of two solutions. You are so generous with your knowledge. Thanks!

      Edit: Implemented the BEGIN block solution, works perfectly. Thanks again.


      The way forward always starts with a minimal test.
Re: Exporting constants from a Moo role
by Corion (Patriarch) on Aug 02, 2018 at 10:42 UTC

    I think that constants are not object-oriented, at least not the way that Moo thinks, and thus they are not exported.

    I think the somewhat hacky, but somewhat yet established way to get at the constants would be to call them as (class) methods, just like Java implements some constants:

    $self->INDEX_FOO # or MyClass->INDEX_FOO

    Of course, you then incur the penalty of making a method call where a real constant could even have been inlined.

    I'm not sure how well Moo (and Moo::Role) and Exporter mix, but maybe you can implicitly export your constants to all modules using your role:

    package MyRole; use Moo::Role; use constant INDEX_FOO => 42; use Exporter 'import'; our @EXPORT = ('INDEX_FOO');

    If all else fails, you'll have to create a special module that just contains the constants...

      Thank you Corion.

      The way forward always starts with a minimal test.
Re: Exporting constants from a Moo role
by anonymized user 468275 (Curate) on Aug 02, 2018 at 12:08 UTC
    One of the features of Moo (and Moose) is to define attributes as readonly with a default value. So it seems only natural that the authors of Moose and Moo would expect such values to be exported as readonly accessors instead of explicitly using constants.

    Update: For example:-

    package MyRole; use Moo::Role; use strictures 2; has 'INDEX_FOO'=> ( is => 'ro', isa => 'Int', default => 42 );

    One world, one people

      You'd probably also want to prevent people from overriding the constant using:

      my $obj = MyClass->new(INDEX_FOO => 43);

      To do this, use:

      has 'INDEX_FOO'=> ( is => 'ro', default => 42 init_arg => undef, );

      But by that point, you have to ask, why not just do this?

      sub INDEX_FOO () { 42 }

        Thanks Toby! init_arg => undef is a new one on me. Why do you need to declare a default value (or what is the point) when you use that parameter to the attribute?

        I quite often use simple subs for constants if only because the syntax to declare them is simpler and shorter. I guess the author of this code (which is used to access the elements of a work queue job) prioritized simpler and shorter syntax at the calling end, preferring $self->job->[INDEX_FOO] to $self->job->[ $self->INDEX_FOO ] ...

        I think I like the solution you showed in your other reply. Thanks again!


        The way forward always starts with a minimal test.

      Thank you, I quite agree. This is inherited code and I wanted to understand the limitations before changing it.


      The way forward always starts with a minimal test.