in reply to Re^2: Silencing specific warnings when executing coderef?
in thread Silencing specific warnings when executing coderef?

"Will you come to Sofia?"

If someone pays me to, sure.

"Dunno if caller is somehow safely overwritable"

It can, Sub::Uplevel does this (and documents the caveats).

"Did you notice that Jesse Luehr's module (thanks for showing, BTW) explicitly warns about using it?"

It was my bug report that led him to add the warning. The issue can be worked around with PadWalker though, and that's what I do in the section of my example commented Fix Parse::Keyword's handling of closed over variables. I use Parse::Keyword in a few projects, and with a bit of care, PadWalker seems to be able to fix this problem every time.

The basic problem can be illustrated with this code sample:

use v5.14; my (@x, @y); for my $i (1..3) { push @x, sub { 1 }; push @y, sub { $i }; } say @x; say @y;

It will produce output something like this:

CODE(0x9c3cce8)CODE(0x9c3cce8)CODE(0x9c3cce8) CODE(0x9c2b228)CODE(0x9c2bd08)CODE(0x9c3c9d8)

You'll notice that @x has the same reference three times, while @y has three different references. This is because at compile-time, when a coderef is encountered, and Perl notices that it closes over a variable, it compiles it into a sort of "proto-coderef". Then when the coderef is hit at run-time (in the example above, the execution of the for loop), Perl sees that it's got a proto-coderef, and fixes up the closed over variables to point to the right data. If the coderef doesn't close over any variables, then it can take the shortcut of reusing the same coderef each time it's encountered at run-time.

Anyway, the parse_* functions in Parse::Keyword essentially only give you a proto-coderef. If this closes over any lexical variables, you need to do the run-time "point to the right data" bit yourself. So that's what I do in the example.

If you comment out the PadWalker lines, you'll get warnings about the variables in $x..$y being undefined, this is because without PadWalker the list generation coderef $list is just a proto-coderef compiled before $x and $y have been given any values.

Anyway, I'm probably boring everybody now. I've just used Parse::Keyword more than most people. Despite its shortcomings, I still think there's life in it.

use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name

Replies are listed 'Best First'.
Re^4: Silencing specific warnings when executing coderef?
by LanX (Saint) on Mar 23, 2014 at 22:18 UTC
    > If someone pays me to, sure.

    I'll send you a postcard! :)

    > Anyway, I'm probably boring everybody now.

    not me! :)

    Also because a week ago I started using exactly these closed_over features of PadWalker and this worries me a little.

    what does "when the coderef is hit at run-time" exactly mean?

    Lets say

    a) I use B::Deparse to deparse and reeval the body of a named sub in a phase before runtime (e.g. CHECK)

    b) the function has "normal" closed-over variables, i.e. not depending on run-time variables like from a for-loop.

    Do I risk that PadWalker gets it wrong if I transfer the closed-over vars from old to new sub?

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      "what does "when the coderef is hit at run-time" exactly mean?"

      There are two important phases that Perl goes through when executing a file: compile-time (a.k.a. BEGIN) and run-time. (Perl actually has a few others like UNITCHECK, CHECK, INIT and END, but I mostly consider those to be subphases of the two big ones.)

      In the compile phase, Perl tokenises and parses the script, turning it into an in-memory optree (what might be called an abstract syntax tree in most programming languages). In the runtime phase, Perl steps through the optree, executing each operation in it.

      So in the compile phase, when Perl sees something like this:

      my $code = sub { $x }; # $x is closed over

      It will compile the sub { $x } into a GV structure in memory; the proto-coderef. The value of $x is not yet known, so the coderef is useless as-is. (But it still needs to be parsed and compiled during this phase because otherwise Perl couldn't determine where the coderef ends, and thus where to resume parsing the code after the coderef. It can't just look ahead for a closing brace because print "}".)

      Later on, at run-time, when it's going through the optree, it has to do an assignment to $x. It evaluates the right-hand side of the assignment, which is a coderef. It notices that the coderef is a proto-coderef, so it clones the proto-coderef and fixes up the closed-over variables.

      It's possible to see this by looking at the coderef's flags using Devel::Peek. Compare the outputs of:

      $ perl -MDevel::Peek -e'my $x; Dump(sub {1})' $ perl -MDevel::Peek -e'my $x; Dump(sub {$x})'

      "Do I risk that PadWalker gets it wrong if I transfer the closed-over vars from old to new sub?"

      Your main problem doing this will be our variables rather than lexicals.

      You may be interested in this StackOverflow question, and also in talking to Graham Knop (HAARG on CPAN) who has been looking at this problem with regards to improving Sub::Quote. He's had some success with our variables, albeit using some very ugly techniques (capturing and parsing the output of Devel::Peek!!), and I believe has managed to get everything to work with no non-core dependencies. His code is here.

      use Moops; class Cow :rw { has name => (default => 'Ermintrude') }; say Cow->new->name