6

So I was going about my Moosey business and I thought hey might be nice to use a constant in these places where I'm using numbers, to make it clear what these numbers mean or in case they change later

So in the parent class I added the standard 'use constant'

package Parent;
    use constant {
        NO_LEVEL => 0,
        MY_LEVEL => 1,
        YOUR_LEVEL => 2,
    };

package Child;
extends 'Parent';

#just to demonstrate that child can or cannot access the constant
sub printMyLevel{
 print MY_LEVEL;
}

but the child class is not aware of the constants set in the parent! doh!

I'm guessing I have to do some Moose magic to get this to work right, or something else entirely. My searching on this issue didnt pull up any results =/

qodeninja
  • 10,946
  • 30
  • 98
  • 152

5 Answers5

11

Constants are subroutines.

{
    package Parent;
    use Moose;
    use namespace::autoclean;

    use constant {
        NO_LEVEL => 0,
        MY_LEVEL => 1,
        YOUR_LEVEL => 2,
    };
    __PACKAGE__->meta->make_immutable;
};

{
    package Child;
    use Moose;
    use namespace::autoclean;

    extends 'Parent';

    sub printMyLevel {
        my $self = shift;
        my $class = ref $self;

        print $class->MY_LEVEL;
    }
    __PACKAGE__->meta->make_immutable;
}

package main;

my $child = Child->new;
$child->printMyLevel;

Keep in mind that constants are subroutines with an empty prototype. perl takes advantage of this to inline them during compilation. However, method calls disregard prototypes, and therefore inheritable constants accessed this way would not be inlined.

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
  • Why do you put the outer { } on your package? I started doing this and someone harped me that it wasnt necessary. p.s. I'd like to access the constants directly in the child class so it looks like i have to use $self->CONSTANT right? – qodeninja Oct 19 '11 at 17:16
  • 1
    @nodebunny I enclosed the packages in a scope because all of this is in a single file. Since this is class data, I like to make that explicit. `$self->CONSTANT` will work, obviously. – Sinan Ünür Oct 19 '11 at 17:17
  • interesting, this seems like a perl idiom. then again I havent really gotten the idea of blocks down yet. {} <== is a block yeah? +1 for the full example. – qodeninja Oct 19 '11 at 17:21
  • 1
    @nodebunny I cannot claim to know what is idiomatic when it comes to `Moose`. However, this is how I would have done it. – Sinan Ünür Oct 19 '11 at 17:24
  • 4
    As of perl 5.14 `package` can take a block as an argument: http://perldoc.perl.org/perl5140delta.html#Syntactical-Enhancements – Joel Berger Oct 19 '11 at 19:10
  • 2
    +1, very thorough answer. I would just add that constant methods are significantly slower than constant subroutines. Since perl's optimizer will not inline the value, perl will have to call the method each time. – Eric Strom Oct 20 '11 at 01:10
6

This is actually mentioned in the documentation, if only in passing:

"Constants belong to the package they are defined in. To refer to a constant defined in another package, specify the full package name, as in Some::Package::CONSTANT. Constants may be exported by modules, and may also be called as either class or instance methods, that is, as Some::Package->CONSTANT or as $obj->CONSTANT where $obj is an instance of Some::Package. Subclasses may define their own constants to override those in their base class."

Sinan Ünür
  • 116,958
  • 15
  • 196
  • 339
Ilmari Karonen
  • 49,047
  • 9
  • 93
  • 153
  • so i could do Parent::CONSTANT too eh? I think I like that better. I was looking in the CPAN/Moose docs thinking there was a Moose way to do it but this works too. +1 for the doc – qodeninja Oct 19 '11 at 17:26
  • `Parent::CONSTANT` would not allow you to override it in child classes. It all depends on what you want. – Sinan Ünür Oct 19 '11 at 17:33
  • 1
    @Sinan: On the other hand, `Parent::CONSTANT` does have the advantage that it can be resolved at compile time. – Ilmari Karonen Oct 20 '11 at 13:43
  • @IlmariKaronen But whether the constants should be overridable is a design question one step removed from "which is faster". – Sinan Ünür Oct 20 '11 at 16:57
4

Call them as a method.

sub printMyLevel{
    my ( $self, ) = $_;
    print $self->MY_LEVEL;
}
gpojd
  • 22,558
  • 8
  • 42
  • 71
4

Since the constants are subroutines and you can get inheritance by calling them as methods bit has been covered to death already, here is a different spin on things.

If you know you are only working in a single file, you can use lexical constants to bridge packages:

package Parent;
    our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL);
    *NO_LEVEL   = \0;  # this split declaration installs aliases to numbers
    *MY_LEVEL   = \1;  # into the lexicals.  since numbers are constants
    *YOUR_LEVEL = \2;  # to perl, the aliased names are also constants

package Child;

# just to demonstrate that anything below can access the constants
sub printAll {
    print "$NO_LEVEL $MY_LEVEL $YOUR_LEVEL\n";
}

Child->printAll; # 0 1 2

eval {$NO_LEVEL = 3} or print "error: $@\n";
# error: Modification of a read-only value attempted at ...

If you don't need perl to die when assigning to the constant, the our declaration gets a bit simpler (and could be a my):

our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);

You can bring back the constant nature while still using the terse syntax with a little magic:

my $constant = sub {Internals::SvREADONLY($_[$_], 1) for 0 .. $#_};

package Parent;
    $constant->(our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2));

package Child;

# just to demonstrate that anything below can access the constants
sub printAll {
    print "$NO_LEVEL $MY_LEVEL $YOUR_LEVEL\n";  # interpolates :)
}

Child->printAll; # 0 1 2

eval {$NO_LEVEL = 3} or print "error: $@\n";
# error: Modification of a read-only value attempted at ...

You can of course omit the $constant coderef and inline the magic:

package Parent;
    Internals::SvREADONLY($_, 1)
        for our ($NO_LEVEL, $MY_LEVEL, $YOUR_LEVEL) = (0, 1, 2);
Eric Strom
  • 39,821
  • 2
  • 80
  • 152
  • +1 for a complete answer. This really helped me understand the situation here. Thank you Eric! – qodeninja Oct 21 '11 at 18:24
  • @EricStrom *That* is some ninja grade perl. If I understand it correctly, and I'd be surprised if I did, unless you *really* need constants, they're kind of a pain. That is, in C, you get some performance enhancements from the compiler when using constants, but in perl, they get looked up the same as a regular variable (more or less). Is this correct? – Erik Bennett Mar 09 '19 at 09:57
2

Inheritance affects methods calls ($x->m), period.

ikegami
  • 367,544
  • 15
  • 269
  • 518