Re: Little Perl Mysteries: what's your answer?
by robartes (Priest) on Oct 30, 2002 at 22:21 UTC
|
And my 2 cents worth..
A function prototyped to accept no arguments is treated as a candidate for inlining by the Perl compiler. In this case sub foo(){2} will be inlined because it returns a constant. The snippet will thus print 2, because print foo(); is turned into print 2; by the compiler, no matter what you do to the definition of sub foo later on at runtime. |
Nice one, Ovid!
CU Robartes- | [reply] [Watch: Dir/Any] [d/l] [select] |
|
| [reply] [Watch: Dir/Any] [d/l] |
Re: Little Perl Mysteries: what's your answer?
by Juerd (Abbot) on Oct 31, 2002 at 10:06 UTC
|
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.
| [reply] [Watch: Dir/Any] [d/l] [select] |
|
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. | [reply] [Watch: Dir/Any] [d/l] [select] |
|
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.
| [reply] [Watch: Dir/Any] [d/l] |
|
| [reply] [Watch: Dir/Any] |
Re: Little Perl Mysteries: what's your answer?
by dws (Chancellor) on Oct 30, 2002 at 22:14 UTC
|
My guess is that...
... prototypes are enforced at compile time. I wonder if part of enforcement has the side-effect of "pickling" a reference to the subroutine to be invoked. If that's the case, swapping the typeglob at runtime would have no effect.
|
| [reply] [Watch: Dir/Any] |
Re: Little Perl Mysteries: what's your answer?
by Aristotle (Chancellor) on Oct 30, 2002 at 21:52 UTC
|
| [reply] [Watch: Dir/Any] |
Re: Little Perl Mysteries: what's your answer?
by PodMaster (Abbot) on Oct 31, 2002 at 04:24 UTC
|
I see no problem here
perl -e "sub foo(){2};BEGIN{*::foo=sub(){3};}print foo()"
The saying "don't use prototypes unless you know why you shouldn't" still applies.
____________________________________________________ ** The Third rule of perl club is a statement of fact: pod is sexy. | [reply] [Watch: Dir/Any] [d/l] |
Re: Little Perl Mysteries: what's your answer?
by Mr. Muskrat (Canon) on Oct 30, 2002 at 22:47 UTC
|
Okay, I feel stupid now...
The magic happens during the compile phase and not the execution phase! |
That is brilliant!
| [reply] [Watch: Dir/Any] |
Re: Little Perl Mysteries: what's your answer?
by Jenda (Abbot) on Oct 30, 2002 at 22:26 UTC
|
Ahhh ... inlined constants ... good gotcha. |
Jenda
| [reply] [Watch: Dir/Any] |
Re: Little Perl Mysteries: what's your answer?
by John M. Dlugosz (Monsignor) on Oct 31, 2002 at 22:29 UTC
|
Before reading any other replies,
I'd say 2, because the call is statically bound by the compiler and it doesn't look up the symbol table entry at all.
I recall this issue from the docs about overriding core functions, and when you have to declare something beyond what importing normally does.
—John | [reply] [Watch: Dir/Any] |
Re: Little Perl Mysteries: what's your answer?
by Joost (Canon) on Nov 01, 2002 at 14:30 UTC
|
Perl 5.8.0 linux-thread-multi croaks with a spoiler
| [reply] [Watch: Dir/Any] [d/l] [select] |
Re: Little Perl Mysteries: what's your answer?
by rir (Vicar) on Oct 31, 2002 at 04:05 UTC
|
The () have no effect on the anonymous sub, a sub ref.
I'm wrong.
| [reply] [Watch: Dir/Any] |