http://qs1969.pair.com?node_id=11143892

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

Hello dear monks,

in my latest project I wanted to replace hard coded literals with constants.
In one case there was a simple loop, which used an expression with a constant for the terminal range value.
Here i stumbled upon an unexpected behavior.
Can someone shed some light on what is going on?

Thanks a lot!

This test program

use strict; use warnings; sub ten { 10 } print "9 .. 10 - 1 without constant\n"; for my $i (9 .. 10 - 1) { print $i, "\n"; } print "-> expected\n\n"; print "9 .. 10 - 1 with constant()\n"; for my $i (9 .. ten() - 1) { print $i, "\n"; } print "-> expected\n\n"; print "9 .. 10 - 1 with constant\n"; for my $i (9 .. ten - 1) { print $i, "\n"; } print "-> unexpected\n\n"; print "9 .. 10 with constant\n"; for my $i (9 .. ten) { print $i, "\n"; } print "-> expected\n\n";
(run with Strawberry Perl 5.26.2) yields:
9 .. 10 - 1 without constant 9 -> expected 9 .. 10 - 1 with constant() 9 -> expected 9 .. 10 - 1 with constant 9 10 -> unexpected 9 .. 10 with constant 9 10 -> expected
It seems literals cannot simply be replaced by defined constants, which i consider rather unfortunate.

Replies are listed 'Best First'.
Re: replacing literals with constants
by Corion (Patriarch) on May 14, 2022 at 09:15 UTC

    Deparsing the code shows what Perl sees (in tmp.pl):

    #!/usr/bin/perl -w use strict; sub ten { 10 } print "9 .. 10 - 1 with constant\n"; for my $i (9 .. ten - 1) { print $i, "\n"; } print "-> unexpected\n\n";

    gives when deparsed:

    corion@outerlimits:~/Projekte$ perl -MO=Deparse tmp.pl BEGIN { $^W = 1; } use strict; sub ten { 10; } print "9 .. 10 - 1 with constant\n"; foreach my $i (9 .. ten(-1)) { print $i, "\n"; } print "-> unexpected\n\n"; tmp.pl syntax OK

    Since you don't have a prototype on your constant, the -1 gets interpreted as an argument to your subroutine call ten. The fix is to use a prototype, or to use the constant module, which does the same:

    sub ten() { 10 };

    ... or ...

    use constant ten => 10;
      Thanks a lot!
      So, I will always use prototypes for constants from now on.

        G'day hexcoder,

        It is a convention, but not a requirement, to use all uppercase for constants. This makes it easier to identify constants in your code: TEN stands out more than ten. See the examples in constant.

        "So, I will always use prototypes for constants from now on."

        For simple values (e.g. numbers, strings, etc.) I tend to use constant. For example:

        use constant { ONE => 1, TWO => 2, ... NINE => 9, TEN => 10, };

        This is a lot simpler than coding:

        sub ONE () { 1; } sub TWO () { 2; } ... sub NINE () { 9; } sub TEN () { 10; }

        I do use subroutines with an empty prototype on occasions. This would be when more complicated code is required. Consider the following example that creates a singleton object.

        { my %constructor_args; BEGIN { # ... code to populate %constructor_args ... } sub OBJ () { Some::Module::->new(\%constructor_args); } }

        Now, OBJ can be used (read-only) wherever the singleton instance is required, but cannot be modified; and %constructor_args cannot be accessed anywhere else in the code.

        That's a somewhat contrived example; however, I hope it gives you an idea of where a subroutine with an empty prototype might be used for a constant (where use of the constant pragma might be unwieldy or impractical).

        Here (in the spoiler) is an actual implementation of that. It's still rather contrived, as there are much better ways of doing this, but it does show the process. In this example, a single Text::CSV object is used as the value of a constant which processes two CSV (pipe-separated) files. (You might want to see Inline::Files if you're unfamiliar with that module.)

        — Ken

        why not just use constant which is self describing?

        many maintainers - like you - will struggle to understand the "magic" of empty prototypes.

        Cheers Rolf
        (addicted to the Perl Programming Language :)
        Wikisyntax for the Monastery

Re: replacing literals with constants
by tybalt89 (Monsignor) on May 14, 2022 at 09:13 UTC
    sub ten () { 10 } # or your function will take arguments
Re: replacing literals with constants (empty prototype needed)
by LanX (Saint) on May 14, 2022 at 09:25 UTC
    First of all, this ...
    • sub ten {10}
    is not a constant.

    For constant folding (optimization at compile time) to happen, you need a a prototype of () , to make sure the result doesn't depend on input.

    • sub ten() {10}
    Otherwise arguments are allowed, which is biting you here!

    The following demos with B::Deparse will demonstrate what is happening

    C:\tmp>perl -MO=Deparse,-p -e"sub ten {10}; print ten() - 1" sub ten { 10; } print((ten() - 1)); # <-- +- call no args -e syntax OK C:\tmp>perl -MO=Deparse,-p -e"sub ten {10}; print ten - 1" sub ten { 10; } print(ten((-1))); # <-- +- call with args -e syntax OK C:\tmp>perl -MO=Deparse,-p -e"sub ten() {10}; print ten - 1" sub ten () { 10; } print(9); # <-- +- constant folding -e syntax OK C:\tmp>perl -MO=Deparse,-p -e"sub ten {10}; 9 .. ten - 1" sub ten { 10; } (9 .. ten((-1))); # <-- +- not what you wanted -e syntax OK C:\tmp>

    Cheers Rolf
    (addicted to the Perl Programming Language :)
    Wikisyntax for the Monastery

    updates

  • s/code folding/constant folding/g
Re: replacing literals with constants
by atcroft (Abbot) on May 14, 2022 at 16:10 UTC

    If there is one thing I have learned (through hard-earned experience), DON'T ASSUME. If you want to ensure operations occur in the order you intend, use parentheses to force it. It makes it much more likely to get the result you desire. That with the suggestions of tybalt89, Corion, kcott, and LanX I believe will give you the results you want.

    Hope that helps.

      G'day atcroft,

      "DON'T ASSUME"

      ++ I could not agree with this more.

      Decades of experience have told me that "Things will never change" is the catch cry of the damned. It doesn't matter how much you attempt to advise management otherwise, you find them unmoved.

      Here's a real world example. I started a new job and recommened moving from font to CSS; heard the "Things will never change"; and was told to stop rocking the boat and suggesting changes they couldn't afford. A few weeks later, their major sponsor changed their corporate colours. After a decision to slightly change their corporate colour, the 70,000 code changes required, versus my singular one CSS change, was a better solution.

      — Ken