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

Let's suppose you have an abstract class, say, RestApiRequest. Your class' user should interit it (let's name it MyRestApiRequest) and implement some methods (for example "response_callback" for handle response), and also define some parameters to tune class behaviour - in our example it will be "timeout" for http request.

This timeout is not per-object, but global for all objects of user's class (same timeout for all API requests for this API vendor). In this case good solution is not to pass timeout to constructor and not to set timeout as object field in constructor, but to define MyRestApiRequest::timeout which will return timeout (a scalar), usually it will be a constant:

package MyRestApiRequest; use parent RestApiRequest; sub response_callback { # some code } sub timeout { 42 } 1;

Question is:

What about:

package MyRestApiRequest; use parent RestApiRequest; use constant timeout => 42; sub response_callback { # some code } 1;

i.e. we have constant here, instead of method. techncally it's same. Is this acceptable? Good style or not? Any drawbacks? Have you seen this before?

Replies are listed 'Best First'.
Re: Using constants as methods
by AnomalousMonk (Archbishop) on Mar 13, 2016 at 15:14 UTC

    The constant pragma creates a subroutine that has an empty prototype and fulfills a few other requirements that allow it to be inlined. A feature of the  -> method invocation operator is that it ignores prototypes, so there is no question of any inlining happening with a method call. E.g.:

    c:\@Work\Perl>perl -le "use warnings; use strict; ;; print qq{Perl ver.: Strawberry $]}; ;; package XX { use constant to_1 => 42; use constant to_2 => 999; } ;; package YY { use parent -norequire, 'XX'; use constant to_2 => 137; } ;; my $y = bless {} => 'YY'; ;; print $y->to_1; print $y->to_2; ;; print YY->to_1; print YY->to_2; ;; print XX::to_1; print XX::to_2; print YY::to_2; ;; print YY::to_1; " Name "YY::to_1" used only once: possible typo at -e line 1. Perl ver.: Strawberry 5.014004 42 137 42 137 42 999 137 print() on unopened filehandle to_1 at -e line 1.
    What's going on is clarified by a deparse:
    c:\@Work\Perl>perl -MO=Deparse,-p -le "use warnings; use strict; ;; print qq{Perl ver.: Strawberry $]}; ;; package XX { use constant to_1 => 42; use constant to_2 => 999; } ;; package YY { use parent -norequire, 'XX'; use constant to_2 => 137; } ;; my $y = bless {} => 'YY'; ;; print $y->to_1; print $y->to_2; ;; print YY->to_1; print YY->to_2; ;; print XX::to_1; print XX::to_2; print YY::to_2; ;; print YY::to_1; " Name "YY::to_1" used only once: possible typo at -e line 1. BEGIN { $/ = "\n"; $\ = "\n"; } sub YY::to_2 () { 137 } sub XX::to_2 () { 999 } sub XX::to_1 () { 42 } use warnings; use strict 'refs'; print("Perl ver.: Strawberry $]"); package XX; sub BEGIN { require constant; do { 'constant'->import('to_1', 42) }; } use constant ('to_2', 999); package main; {;}; package YY; sub BEGIN { require parent; do { 'parent'->import((-'norequire'), 'XX') }; } use constant ('to_2', 137); package main; {;}; (my $y = bless({}, 'YY')); print($y->to_1); print($y->to_2); print('YY'->to_1); print('YY'->to_2); print(42); print(999); print(137); print(YY::to_1 $_); -e syntax OK
    Either form of method invocation (by class name or object reference) compiles the same code as for any similar method invocation, with inheritance as appropriate but no inlining. A fully qualified invocation allows inlining, but inheritance is defeated and an expression like  YY::to_1 just makes Perl confused and cranky.

    Defining a class method as a constant "works" in the same way as any other method definition. It seems to me that the only advantage of this practice is to introduce a bit of obfu into your code and so perhaps ease the tedium that is the daily portion of some future maintainer. Whether he or she will appreciate your consideration is another matter.


    Give a man a fish:  <%-{-{-{-<

Re: Using constants as methods
by Anonymous Monk on Mar 13, 2016 at 09:01 UTC

    Personally, I would assume that although today you're thinking it's a global constant, someone might want to be able to change the timeout on a per-object basis someday. So, even though constant basically works by creating an inlinable subs, I would suggest that you write it the first way, and document it. The reason being that that way, it would be more clear to a future maintainer (including yourself) that the option exists to override the method in subclasses, or turn it into a getter/setter later. (On the other hand, the distinction between the two is only small - constant even mentions "Subclasses may define their own constants to override those in their base class." - so it's also a matter of taste.)

Re: Using constants as methods
by LanX (Saint) on Mar 13, 2016 at 01:42 UTC
    Not coding much OOP myself, but it depends what you want.

    This constant won't be available from other files, especially it won't be inherited, but the getter method would.

    So shall it be private or not?

    edit

    Oh! Sorry I misread constant as Readonly , never mind.

    I don't think that use constant is a good idea because it does the same but in an obfuscated way.

    Cheers Rolf
    (addicted to the Perl Programming Language and ☆☆☆☆ :)
    Je suis Charlie!

      This constant won't be available from other files, especially it won't be inherited, but the getter method would.
      No! It works fine (when we talk about method)! Seems it's available from any other place, and can be inherited. Example:
      package XX; use constant timeout => 42; 1; package YY; use parent -norequire, 'XX'; 1; package main; use strict; use warnings; my $y = bless +{}, "YY"; print $y->timeout; # fine! 1;
      So shall it be private or not?
      Not necessary.
        Yeah I noticed already in the meantime, please see update.

        Sorry its late here... :)

        Cheers Rolf
        (addicted to the Perl Programming Language and ☆☆☆☆ :)
        Je suis Charlie!