This is PerlMonks "Mobile"

Beefy Boxes and Bandwidth Generously Provided by pair Networks
Keep It Simple, Stupid
 
PerlMonks  

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

Hi

We had a confused discussion at last Darmstadt Perlmongers meeting, about how the callers context of a sub effects the context within the sub.

Side question was how to reliably force void-context.

Though the perldocs of return could be clearer, I came up with basic rules I wanna share and discuss here:

1. Only returned statements/lines have the callers context.

2. All other lines are per default in void context.

3. Implicitly returned statements - i.e. final but without return keyword - are like in case 1 also in callers context.

The last point can be sometimes confusing, because with nested control flow it's not always instantly clear where a sub possibly drops out.

Please see the following testcode and output for deeper understanding and as a base for your own tests.

Questions, discussions and modifications are welcome.

# -------------------- # force context # -------------------- sub VOID (&) { print "VOID:\t"; $_[0]->(); return; } sub LIST (&) { print "LIST:\t"; () = $_[0]->(); return; } sub SCALAR (&) { print "SCALAR:\t"; scalar $_[0]->(); return; } # -------------------- # tests # -------------------- sub tst_context { unless (defined wantarray) { print "is void\n"; } elsif (wantarray) { print "is list\n"; } else { print "is scalar\n"; } } SCALAR { tst_context() }; LIST { tst_context() }; VOID { tst_context() }; # -------------------- # implicit_returns # -------------------- sub implicit_returns { print "implicit_returns(@_)\t"; if ($_[0]) { tst_context() # caller's context } else { tst_context() # caller's context } } SCALAR { implicit_returns(1) }; SCALAR { implicit_returns(0) }; LIST { implicit_returns(1) }; LIST { implicit_returns(0) }; VOID { implicit_returns(1) }; VOID { implicit_returns(0) }; # -------------------- # no_implicit_returns # -------------------- sub no_implicit_returns { print "no_implicit_returns(@_)\t"; if ($_[0]) { tst_context() # void context } else { tst_context() # void context } return; # last execution! } SCALAR { no_implicit_returns(1) }; SCALAR { no_implicit_returns(0) }; LIST { no_implicit_returns(1) }; LIST { no_implicit_returns(0) }; VOID { no_implicit_returns(1) }; VOID { no_implicit_returns(0) };

Output:

SCALAR: is scalar LIST: is list VOID: is void SCALAR: implicit_returns(1) is scalar SCALAR: implicit_returns(0) is scalar LIST: implicit_returns(1) is list LIST: implicit_returns(0) is list VOID: implicit_returns(1) is void VOID: implicit_returns(0) is void SCALAR: no_implicit_returns(1) is void SCALAR: no_implicit_returns(0) is void LIST: no_implicit_returns(1) is void LIST: no_implicit_returns(0) is void VOID: no_implicit_returns(1) is void VOID: no_implicit_returns(0) is void

UPDATE: same rules apply to evals:

# -------------------- # eval # -------------------- LIST { eval "print 'eval '; return tst_context(); 'never executed'" }; #-> LIST: eval is list

Cheers Rolf

Replies are listed 'Best First'.
Re: Context propagation into subs and evals
by sundialsvc4 (Abbot) on Feb 20, 2012 at 14:11 UTC

    As far as I am aware, there are only two situations in which the context of a subroutine can affect or refer in any way to the context of its caller, viz:

    1. If the caller provides a reference to a particular value, explicitly or implicitly, then the subroutine may employ the reference, as any other piece of code equally can.
    2. Perl supports closures, which consist of CODE references as in the following one-liner example:
      sub bar { my $foo="bar"; return sub { print "foo is $foo\n"; } } my $fn = bar(); &$fn; foo is bar
      Note how the function returned by sub bar references the local variable $foo and how the value remains accessible when the main-program invokes the subroutine (“closure”...) denoted by that result.

    eval has two distinct meanings:   on-the-spot evaluation of the contents of a character string, and trapping of run-time errors.   In both instances, AFAIK, they are treated as lexical blocks occurring at that point.   (In the latter case, the block would be the same even if the keyword eval did not precede it.)

      I think you are defining the term "context" differently.

      It might be used like this when discussing variable pads and closures...(?)

      ...but that's not my point.

      Please see the definition in perlglossary to understand my interpretation:

      context
           The surroundings, or environment.  The context given by the
           surrounding code determines what kind of data a particular
           "expression" is expected to return.  The three primary contexts are
           "list context", "scalar context", and "void context".  Scalar
           context is sometimes subdivided into "Boolean context", "numeric
           context", "string context", and "void context".  There’s also a
           "don’t care" scalar context (which is dealt with in Programming
           Perl, Third Edition, Chapter 2, "Bits and Pieces" if you care).
      

      UPDATE: see also wantarray

      Cheers Rolf

Re: Context propagation into subs and evals
by moritz (Cardinal) on Feb 21, 2012 at 09:59 UTC

    It seems that what you wrote is correct. A possible source of confusion is that some part of the context is partially determined at compile time, and partially at run time.

    In particular, the void context of all lines except the last inside a subroutine is determined at compile time, and the "Useless use of ... in void context" warnings from use warnings or -w are based on that:

    $ perl -wce 'sub f { 1 + 1; 1}; f' Useless use of a constant (2) in void context at -e line 1. -e syntax OK

    At run time, the context of the last expression or the return value is evaluated in the same context as the the whole sub, and that context information recursively flows inwards into any subs. So if you have

    sub f() { g() }

    then the context of g is that of f, determined dynamically at run time.

Re: Context propagation into subs and evals
by repellent (Priest) on Feb 20, 2012 at 21:28 UTC
    In implicit_returns and no_implicit_returns, did you mean to have an explicit return as in return tst_context() when $_[0] is true?
      implicit_returns() has two implicit returns, because the calls to tst_context() could be last statement of the sub.

      Since no_implicit_return() has an explicit return at the end which must be passed, disabling the implicit returns.

      look here

      sub tst { 5 } sub tst { return 5 }
      Both functions are equivalent, but in the latter the return is explicit.

      Cheers Rolf

        Right, I forgot about the return; so I've striked off my question about no_implicit_return.

        As for implicit_return, I guess I got confused as to the purpose of the if statement. Both the true and false code paths lead to an implicit return of the call to tst_context. Me thinks why bother having the if at all? :-)