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

Recently, I had an interesting concept pop into my mind. Is it possible to use constants as package methods, and if so, is there any compelling reason why people don't do that more often? A little research showed me that yes, it is possible to use constants as package methods, so now I open the question up to my fellow monks:

Is it a BAD THING(tm) to use constants as package methods?

Pros:

Cons:

Example:

test.pl
#!perl use strict; use warnings; use My::Package; use My::Package::Derived; local $\ = "\n"; my $item1 = new My::Package; my $item2 = new My::Package::Derived; print $item1->SOME_VALUE; print $item1->SOME_DERIVED_VALUE; print $item1->ANOTHER_DERIVED_VALUE; print '-' x 30; print $item2->SOME_VALUE; print $item2->SOME_DERIVED_VALUE; print $item2->ANOTHER_DERIVED_VALUE;
My/Package.pm
package My::Package; use strict; use warnings; use constant SOME_VALUE => 12; use constant SOME_DERIVED_VALUE => 14; use constant ANOTHER_DERIVED_VALUE => 21; sub new { bless {}, ref($_[0]) || $_[0]; } 1;
Derived.pm
package My::Package::Derived; use strict; use warnings; use My::Package; our (@ISA) = qw[My::Package]; use constant SOME_DERIVED_VALUE => 15; sub ANOTHER_DERIVED_VALUE() { int(rand(100)+1); } 1;

References

2002-03-13 Edit by Corion : Fixed title (almost) per request

Replies are listed 'Best First'.
Re: Is it morally wrong to use consant's as methods in a package?
by Juerd (Abbot) on Mar 13, 2002 at 18:21 UTC

    Let's first define "constant", as Perl doesn't natively have a constant data type.

    A constant in Perl is a subroutine that always returns the same value, often a single, very simple statement. Because of its simplicity, the return keyword is not used in most cases.

    But subroutines are slower than variables, so why use constant CONSTANT (which, in a way, equals sub CONSTANT { ... }) when $CONSTANT would be faster? It's not only because of the read-only nature of constants, because *CONSTANT = \"read only string"; would take care of that. Using constants is mostly because it's generally considered a Good Thing™ to indicate clearly that something has a constant value, and should not be messed with.

    To overcome the speed problem, Perl can inline subroutines. When an inlined subroutine is used, that will be as if it's return value were used as a literal constant, and we can all guess that 2 is faster than calling a subroutine that returns it, or having a variable that has 2 as its value.

    Perl can inline subs, when they have an empty prototypes and have a constant return value (I'm probably a bit wrong here - if you're a more experienced monk reading this, please do correct me). So when you have sub FOO { 1 } and sub () BAR { 1 }, BAR() will be a LOT faster.

    This all seems to have nothing to do with objects and method calls. But it does have. Because objects don't exist at compile time, method calls are done at runtime, which implies that prototypes are not used. That's why you can use constants as method calls in the first place, because normally the () prototype would avoid parameters to be passed, but methods get their object (or class) as their first argument. So that's the first speed loss, because the constant sub can no longer be inlined.

    The second reason why method calls are slow is that Perl has to find out into which package the object is blessed before it can call the appropriate sub.

    #!/usr/bin/perl -w package Foo; use constant CONSTANT => 2; package main; use Benchmark qw(cmpthese); use strict; my $object = bless \(my $dummy), 'Foo'; # Note: I use assignments to avoid the constant in void context # to be optimised away (that would be too fast for Benchmark # to handle (try removing "my $foo =" for fun ;)). cmpthese(-1, { object_method => sub { my $foo = $object->CONSTANT }, class_method => sub { my $foo = Foo->CONSTANT }, direct_sub => sub { my $foo = Foo::CONSTANT } }); __END__ Benchmark: running class_method, direct_sub, object_method, each for a +t least 1 CPU seconds... class_method: 2 wallclock secs ( 1.07 usr + 0.00 sys = 1.07 CPU) @ +401942.99/s (n=430079) direct_sub: 2 wallclock secs ( 1.16 usr + 0.00 sys = 1.16 CPU) @ 33 +89792.24/s (n=3932159) object_method: 1 wallclock secs ( 1.14 usr + 0.00 sys = 1.14 CPU) @ + 548745.61/s (n=625570) Rate class_method object_method direct_sub class_method 401943/s -- -27% -88% object_method 548746/s 37% -- -84% direct_sub 3389792/s 743% 518% --

    And most constants that are to be used in the other package are exported (that's why most constants have some kind of prefix) anyway.

    So in general, I think it's not a very good idea to use constants as methods. But the idea is nice :)

    U28geW91IGNhbiBhbGwgcm90MTMgY
    W5kIHBhY2soKS4gQnV0IGRvIHlvdS
    ByZWNvZ25pc2UgQmFzZTY0IHdoZW4
    geW91IHNlZSBpdD8gIC0tIEp1ZXJk
    

Re: Is it morally wrong to use consant's as methods in a package?
by perrin (Chancellor) on Mar 13, 2002 at 17:50 UTC
    The current implementation of constants pretty much sucks anyway, since it fails in so many places (HERE docs, hash keys, and anywhere else that barewords are stringified). The way you're talking about using it is basically to treat it as an obfuscated method generator. Why not just code the methods explicitly, so that people don't have to know about the implementation of the constant pragma to understand your code?

      I agree that Perl's idea of constants sucks. It is probably because there ain't no such thing as a constant in perl that the whole inlined subroutine thing emerged; it was probably stumbled upon by some hapless developer, and grew into common usage from there.

      The short answer to the original question, which I thought I saw you alluding to the first time I read your response, is that using constants as methods pretty much does away with the compiler's inlining optimization (since a method lookup must now be done at runtime), which is most of why to do it in the first place.

      The whole namespace argument doesn't hold up since it is far clearer to say, Module::CONST_VALUE (and get the benefit of inlining, as well) than $objOfSomeHardToDetermineType->CONST_VALUE.

      dmm

      If you GIVE a man a fish you feed him for a day
      But,
      TEACH him to fish and you feed him for a lifetime
Re: Is it morally wrong to use consant's as methods in a package?
by dragonchild (Archbishop) on Mar 13, 2002 at 17:35 UTC
    Morally wrong? No. Common practice? Nope. Why? Who knows. Probably because a method isn't seen to be constant. Values are constant.

    This trick only works because of the way constants are implemented (functions in the current namespace). If they were implemented differently, this wouldn't work.

    ------
    We are the carpenters and bricklayers of the Information Age.

    Don't go borrowing trouble. For programmers, this means Worry only about what you need to implement.