in reply to Little Perl Mysteries: what's your answer?

Note: spoiler, black text on a black background. Select the text to read it. On some systems, you might have to paste it before it's actually visible. (And I put in some glow-in-the-dark hyperlinks *grin* (code may also be visible, if you defined a background color for it, in your PM CSS)). Update: Grumble. There's an HTML filter that filtered out the bgcolor of my <tr> tag. Putting it in <td> seems to be allowed.

robartes is right.

(Note: I'll be using the words 'compile-time' and 'run-time', even though there is no clear distinction in Perl which is when.)

The first definition is happening at compile-time, the second is at run-time. One might expect that in print foo(), a subroutine is called, and that that happens at run-time, thus using the new definition of foo().

But there is no subroutine call. Perl tries to be smart, and optimizes the code. The foo() has become a constant, the actual print that does happen is just print 2, as dada points out.

podmaster was able to redefine the function effectively, because BEGIN blocks are executed as soon as possible (i.e. immediately), and redefinition takes place before run-time and before the foo() "call" is encountered. Other compile-time tricks would allow redefinition too, like having the code in a .pm file and using use.

You get the expected 3 if you force Perl to actually call the subroutine . You can do that by using the & sigil. This disables prototype checking and inlining (probably because inlining depends on the prototype).

perl -le'sub foo(){2};*::foo=sub(){3};print &foo()'
Other ways of avoiding Perl to inline foo() are:
print *foo{CODE}(); print eval 'foo()';

(I think I found a bug in my B::Deparse. perl -e'sub foo(){2}' doesn't even display the definition of foo, while dada's Deparse does display inlined subroutines. It's not as if the sub is completely gone: perl -le'sub foo(){2} print *foo{CODE}()' does work as expected. Perl 5.8.0 on linux x86. Please confirm.)

- Yes, I reinvent wheels.
- Spam: Visit eurotraQ.

Replies are listed 'Best First'.
Re: Re: Little Perl Mysteries: what's your answer?
by Ovid (Cardinal) on Oct 31, 2002 at 17:06 UTC

    robartes appears to be the first to have correctly answered the question, but I'll respond to this as you've answered more in depth and you have a question that needs answering.

    The article that I read, by Sean Burke, is about Constants in Perl. It discusses some a traditional compiler optimization known as "constant folding". Essentially, this occurs when, for a given operations, if all of the operands are known at compile time and cannot change, then the compiler goes ahead and precomputes the resulting value to save a bit of time.

    perl -MO=Deparse -e 'print 3*4' print 12; -e syntax OK

    Thus, what appears to be an expression is turned into a literal constant. Note that the 3*4 is now optimized away. There is no longer any remnant of that code. This, naturally, would be even more useful when it occurs in an inner loop, but the mechanism is not perfect and Perl sometimes needs to be helped. See the article for details and the snippet below for an example.

    $ perl -MO=Deparse -e '$i=6;$j=2;print 3*4*$i*$j*3*4' $i = 6; $j = 2; print 12 * $i * $j * 3 * 4; -e syntax OK

    That result is disappointing, but adding the '-p' switch to force parenthese can tell us why Perl is getting a bit confused.

    $ perl -MO=Deparse,-p -e '$i=6;$j=2;print 3*4*$i*$j*3*4' ($i = 6); ($j = 2); print(((((12 * $i) * $j) * 3) * 4)); -e syntax OK

    Tying all of that together with my snippet, we have the following quote from the third Camel.

    Inlining Constant Functions

    Functions prototyped with (), meaning that they take no arguments at all, are parsed like the time built-in. More interestingly, the compiler treats such functions as potential candidates for inlining. If the result of that function, after Perl's optimization and constant-folding pass, is either a constant or a lexically scoped scalar with no other references, then that value will be used in place of calls to that function. Calls made using &NAME are never inlined, however, just as they are not subject to any other prototype effects.

    Because Perl can optimize the sub call away, there is no longer any subroutine for the typeglob to override.

    Juerd wrote:

    I think I found a bug in my B::Deparse. perl -e'sub foo(){2}' doesn't even display the definition of foo, while dada's Deparse does display inlined subroutines. It's not as if the sub is completely gone: perl -le'sub foo(){2} print *foo{CODE}()' does work as expected. Perl 5.8.0 on linux x86. Please confirm.

    I don't believe this is a bug. I ran that on my Linux box at home with both 5.6.1 and 5.8.0 and the latter does not display the definition of the sub, but the former does. I suspect that this is actually an optimization where there is no need to have the sub's code in the bytecode if it's been optimized away. Unfortunately, I cannot find a reference to this in the 5.8.0 docs and thus cannot confirm it.

    Cheers,
    Ovid

    Thanks to larsen for pointing out my typo: s/Contacts/Constants/;

    Join the Perlmonks Setiathome Group or just click on the the link and check out our stats.

      I don't believe this is a bug.

      I think it is a bug because Deparse's output is not executable.

      1;1 juerd@ouranos:~$ perl -e'sub foo () { 42 } print foo(), "-\n"' 42- 1;0 juerd@ouranos:~$ perl -MO=Deparse -e'sub foo () { 42 } print foo() +, "-\n"' print 42, "-\n"; -e syntax OK 1;0 juerd@ouranos:~$ perl -MO=Deparse -e'sub foo () { 42 } print foo() +, "-\n"' | perl -e syntax OK 42- 1;0 juerd@ouranos:~$ perl -e'sub foo () { 42 } print *foo{CODE}(), "-\ +n"' 42- 1;0 juerd@ouranos:~$ perl -MO=Deparse -e'sub foo () { 42 } print *foo{ +CODE}(), "-\n"' print *foo{'CODE'}(), "-\n"; -e syntax OK 1;0 juerd@ouranos:~$ perl -MO=Deparse -e'sub foo () { 42 } print *foo{ +CODE}(), "-\n"' | perl -e syntax OK Undefined subroutine &main:: called at - line 1. 1;255 juerd@ouranos:~$
      (In the last example, *foo{CODE} is undef, and undef->() gives an 'Undefined subroutine &main:: called' error.)

      - Yes, I reinvent wheels.
      - Spam: Visit eurotraQ.
      

Re^2: Little Perl Mysteries: what's your answer?
by Aristotle (Chancellor) on Oct 31, 2002 at 11:04 UTC
    No such trouble here - deparses fine. However, various versions of the module have different defaults for how readable they try to make the output, so you may want to try -MO=Deparse,-x7 to force no translation to be done. If it still doesn't show up, that would be a bug then I guess.

    Makeshifts last the longest.