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

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

Dear Monks,

Having nothing better to do, I wondered whether that "last expression" that is returned by non-empty subroutines according to perlsub is well-defined. So I tested my understanding of that corner of the docs with these one-liners:

% perl -wle 'sub foo { 1 for @_ } print 2 if defined foo()' % perl -wle 'sub foo { 1 for @_ } print 2 if defined foo(3)' 2 % perl -wle 'sub foo { 1 for @_ } print 2 if foo(3) == 0' 2 % perl -wle 'sub foo { 1 for @_ } print 2 if foo(3) eq ""' 2

and I am puzzled. I guess the difference between the first and the rest have to do with the underlying implementation, though I don't actually know it. From a conceptual point of view I wouldn't have been able to predict that behaviour.

Then, IF the same value is returned always in the rest of the cases, I don't understand how can it be at the same time defined, == 0, and eq "" (edited: and not issue the warning Argument "" isn't numeric in numeric eq (==) ...).

So, is "the last expression" well-defined albeit not always apparent? Are those one-liners just showing unespecified behaviour (which would be a valid answer to my original question)?

-- fxn

PS1: To make things more interesting someone in MagNet#perl pointed out that if you change the 1 with a 43 something different a warning is issued:

% perl -wle 'sub foo { 43 for @_ } print 2 if defined foo()' Useless use of a constant in void context at -e line 1.

PS2: I have posted this question in a few places before, but got no definitive answer yet.

Replies are listed 'Best First'.
Re: "last expression" quiz
by sauoq (Abbot) on Oct 19, 2005 at 22:16 UTC
    I don't understand how can it be at the same time defined, == 0, and eq "".

    This is easy. The return value is the empty string !1 as bart says.

    $ perl -le 'print "yes" if defined ""' yes $ perl -le 'print "yes" if 0 == ""' yes $ perl -le 'print "yes" if "" eq ""' yes

    The bigger question you are asking—basically, why the empty string is returned—doesn't seem to have such a clear answer. This has come up recently in the discussion that followed Re^4: Unhappy returns. You'll find a closer look at the more general issue including how other statements are handled there. FWIW, I think it should probably be considered a bug. I'm pretty sure it isn't documented.

    Which is exactly the same as the empty string only, uh, different It doesn't cause warnings where an empty string would.

    Edit: Corrected "empty string" to !1 per bart's response below and added footnote.

    -sauoq
    "My two cents aren't worth a dime.";
    

      Oh, yes, AND not issue a warning:

      % perl -wle 'print 1 if "" == 0' Argument "" isn't numeric in numeric eq (==) at -e line 1. 1
        A for loop typically returns nothing, whatever that means...

        Judging by your results on the warnings, or rather the lack of them, I think the subs return false, thus, !1. That is both equal to "" and numerically equal to 0 without a warning.

        Oh, and a 1 is allowed to be used as a statement without warning. Typical use for them is in a bodyless loop:

        1 while COND;
        as well as as the last statement in a required file/module. Hence that there's no warning for a bare 1;.

      You see, you are wondering about some stuff and learn a lot by side effect. Thank you very much for your answers.

      With regard to the very "last expression", I guess we could conclude perlsub is not accurate there. It is true that some value is always returned, but in the subroutine

      sub foo { 1 for @_ }
      there are only TWO expressions, namely "1", and "@_". The "last expression" by definition should be one of them, but the return value is none of them.

      Does anybody disagree with that conclusion?

        Does anybody disagree with that conclusion?

        I certainly don't. An even better example:

        sub foo { 1 for 1 }
        Now there's only one expression which always evaluates to the same thing, 1, and yet the sub unintuitively returns !1. Certainly a patch is needed. Whether it should just be a doc patch to perlsub or different behavior should be forced, I don't know.

        -sauoq
        "My two cents aren't worth a dime.";
        
Re: "last expression" quiz
by Aristotle (Chancellor) on Oct 19, 2005 at 22:38 UTC

    Check Re^8: Unhappy returns (just updated) for some tests as to what gets returned when. Apparently:

    • If the last thing that happened was an iteration of a for loop, you will get back !1, alias scalar false (it is 0 and an empty string at the same time).
    • If the last thing that happened was an iteration of a while loop, you get back nothing at all.
    • If the last thing that happened was an if block, you get back the value of the last expression evaluated – that would be either the condition, if it evaluated to false, or the last expression from the body of the conditional block.

    In other words, what perlsub says is deceptive and incomplete.

    Update: oh goodness, it gets weirder still:

    • If the last thing that happened was an iteration of the do {} while $cond or do {} until $cond pseudo-loop, you get back a code reference (whether the condition turned out true of false).

    It’s probably safe to say that Perl simply has no rule here, and that what you get back from a sub depends on implementation details of perl.

    Makeshifts last the longest.

      It’s probably safe to say that Perl simply has no rule here, and that what you get back from a sub depends on implementation details of perl.
      Wouldn't it be more accurate to say that returning the value from the last expression in subs works fine. But statements (things like if, for, while) don't have values and return pretty much garbage?

        Maybe, but how do you reconcile that with the behaviour with if blocks, which work precisely as documented?

        Makeshifts last the longest.

        But statements (things like if, for, while) don't have values and return pretty much garbage?

        Well, if works in that it returns the result of the last expression evaluated. It seems that's what looping constructs should do too. Wouldn't it make sense for this sub x { $_ for 10 } to return 10?

        -sauoq
        "My two cents aren't worth a dime.";
        
      If the last thing that happened was an iteration of the do {} while $cond or do {} until $cond pseudo-loop, you get back a code reference (whether the condition turned out true of false).

      That sounds really strange, but sadly I can't reproduce it. I tried

      perl -e 'print sub {my $x=5; do {print "."} while $x--}->()'
      but the sub just seems to return 0. Can you give an example? (Also, what version of Perl are you using?)

        As the node you’re replying to already says: check Re^8: Unhappy returns for code.

        $ perl -v
        
        This is perl, v5.8.7 built for i486-linux

        Makeshifts last the longest.

Re: "last expression" quiz
by Skeeve (Parson) on Oct 19, 2005 at 22:19 UTC
    % perl -wle 'sub foo { 1 for @_ } print 2 if defined foo()'
    foo does nothing. There is no parameter. So undef is returned. So no wonder nothing is printed.

    % perl -wle 'sub foo { 1 for @_ } print 2 if defined foo(3)'
    The loop is run once. So a defined value is returned.

    % perl -wle 'sub foo { 1 for @_ } print 2 if foo(3) == 0'
    The value is defined but it's numerical value is zeroundef == 0

    % perl -wle 'sub foo { 1 for @_ } print 2 if foo(3) eq ""'
    and it's an empty stringundef eq ""

    I think 1 is different from 43 (or 42 or 2 or whatever other number you can come up besides 0) because it's usually used to represent "true".

    s$$([},&%#}/&/]+}%&{})*;#$&&s&&$^X.($'^"%]=\&(|?*{%
    +.+=%;.#_}\&"^"-+%*).}%:##%}={~=~:.")&e&&s""`$''`"e
Re: "last expression" quiz
by QM (Parson) on Oct 21, 2005 at 14:03 UTC
    Regarding the for loop issues (and this may apply to other loop controls as well), it seems that the loop must stop on a false boolean expression. If that's the last expression in the sub, then that should be returned.

    Note that under the hood, all of the loop constructs can be mapped to a single generic construct (which I suspect is what actually happens). Suppose we have this:

    sub x { for my $q (1..10) { print "$q\n"; } }
    While you may think the last thing evaluated is print "$q\n", the for actually has to check that there are no more elements in the list, perhaps making the above code equivalent to:
    sub x { my @_forlist = (1..10); if (@_forlist) { do { my $q = pop @_forlist; print "$q\n"; } until ( not @_forlist ); } }
    So the last expression evaluated is not @_forlist. Which is not explicit in the code!

    Most of the errant behaviors documented in this thread seem to ignore this implicit expression. Perhaps the documentation could emphasize that "last expression" doesn't mean "last explicit expression".

    BTW, does someone have the skills to investigate the optree for examples like these?

    -QM
    --
    Quantum Mechanics: The dreams stuff is made of