Beefy Boxes and Bandwidth Generously Provided by pair Networks vroom
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

Stumped when passing barewords

by crenz (Priest)
on Apr 22, 2004 at 12:00 UTC ( #347313=perlquestion: print w/ replies, xml ) Need Help??
crenz has asked for the wisdom of the Perl Monks concerning the following question:

Fellow monks,

I'm working on a subroutine that I want to pass a bareword as parameter. While doing that, I stumbled upon a weird problem. Let's take this program

use strict; use warnings; sub sub1(*) { print "args(1): @_\n"; sub2(@_); } sub sub2(*) { print "args(2): @_\n"; } sub sub3(*) { print "args(3): @_\n"; sub2(@_); } sub1 Bareword; sub3 Bareword;

and run it. What would you expect is it's output? My expectation was

args(1): Bareword args(2): Bareword args(3): Bareword args(2): Bareword

What was actually printed by the program is

args(1): Bareword args(2): Bareword args(3): Bareword args(2): 1

Can any of the more enlightened monks explain to me why that happens? Obviously, it seems to be caused by the order of definition of the subroutines, but why? This is on v5.8.1-RC3 on Mac OS X 10.3.3, by the way.

Comment on Stumped when passing barewords
Select or Download Code
Re: Stumped when passing barewords
by BrowserUk (Pope) on Apr 22, 2004 at 12:35 UTC

    I don't have an answer, but I get the same results on 5.8.3/Win so it's not a platform thing.

    Re-ordering the declarations of sub2 and sub3 gives your expected output, which is counter-intuative.

    I'm intrigued to see the reasoning behind this one too:)


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "Think for yourself!" - Abigail
      Same on Linux - 5.8.3
Re: Stumped when passing barewords
by diotalevi (Canon) on Apr 22, 2004 at 12:54 UTC

    Doh. If I say it with Devel::Peek I note that the first call to sub2() was compiled when the (*) prototype wasn't known and @_ wasn't coerced into a scalar. The second was compiled when perl expected there to be only a scalar so instead of sub2(@_) you have sub2( scalar @_)

    sub2 returns 1 because print() returns 1. This is 100% mundane. You might try saying print print '' to see what I mean.

    use Devel::Peek; sub sub1(*) { print STDERR "args(1)\n"; Dump($_) for @_; sub2(@_); } sub sub2(*) { print STDERR "args(2)\n"; Dump($_) for @_; } sub sub3(*) { print STDERR "args(3)\n"; Dump($_) for @_; sub2(@_); } sub1 Bareword; sub3 Bareword; sub2(@{[Bareword]});
      In that case why does this have no effect?
      sub sub2(*) { print "args(2): @_\n"; 0; # <--- change is here. }
      The output is from the print statements, not the return values.

      sub2() returns 1, but how is it managing to print out it's own return value?


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail
Re: Stumped when passing barewords
by broquaint (Abbot) on Apr 22, 2004 at 13:04 UTC
    This is purely conjectural but, because at the point of compilation in sub1 the prototype of sub2 has yet to be defined, the args will be passed in as is. However, at the point when sub3 is compiled the prototype has been seen, so the args are munged to fit the prototype so @_ is forced into a scalar context. So if we predeclare sub2 we see consistent behaviour of calling sub2 with @_ e.g
    sub sub2(*); sub sub1(*) { print "args(1): @_\n"; sub2(@_); } sub sub2(*) { print "args(2): @_\n"; } sub sub3(*) { print "args(3): @_\n"; sub2(@_); } sub1 Bareword; sub3 Bareword; __output__ args(1): Bareword args(2): 1 args(3): Bareword args(2): 1
    Although I'm not positive on that explanation, I believe it's something along those lines.

    Update: indeed, if we change the prototype between the compilation of the subs we see the output changes to the expected behaviour of the above explanation e.g

    sub sub2(*); sub sub1(*) { print "args(1): @_\n"; sub2(@_); } sub sub2 { print "args(2): @_\n"; } sub sub3(*) { print "args(3): @_\n"; sub2(@_); } sub1 Bareword; sub3 Bareword; __output__ args(1): Bareword args(2): 1 args(3): Bareword args(2): Bareword
    HTH

    _________
    broquaint

      Yes, if you read perlsub on prototypes carefully it would appear that "*" forces scalar context in the same way that "$" would. Might be worth someone working up an extra sentence which makes an explicit mention of the fact and submitting a patch to p5p.

      Update: And I've just submitted said doc patch.

        broquaint, thanks for the research. It seems you found the phenomenon causing the problem.

        Fletch, I would expect for the bareword to stay the same, even in scalar context. If I change sub 1 to

        sub sub1(*) { print "args(1): @_\n"; print scalar $_[0], "\n"; sub2(@_); }

        the second value printed is Bareword and not 1.

        So am I misunderstanding the spec here, or is it a bug?

Re: Stumped when passing barewords
by halley (Prior) on Apr 22, 2004 at 13:31 UTC
    I'm surprised you're not getting the dreaded "prototype too late" warning. That may be the real bug here.
    use strict; use warnings; foo(2,3,4); sub foo($$$) { print "@_", $/ } __OUTPUT__ main::foo() called too early to check prototype at - line 3. 2 3 4

    --
    [ e d @ h a l l e y . c c ]

      This is because in your example the call to foo can be discerned at compile-time, whereas subroutine calls within other subroutines can't due to perl's dynamic nature, hence the lack of use strict 'subs must exist'.
      HTH

      _________
      broquaint

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://347313]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others avoiding work at the Monastery: (9)
As of 2014-04-19 10:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    April first is:







    Results (480 votes), past polls