in reply to Silencing specific warnings when executing coderef?

It might be possible in XS, but would require some pretty impressive optree manipulation. $SIG{__WARN__} is the only non-insane way to do it, but as you say, introduces some overhead.

One "solution" would be to hide the issue. Assuming you intend to export your sugar functions from a module, you can place some code in your module's import method to unimport exiting warning from its caller. Be polite though and document it.

For what it's worth, even if you can get last to work flawlessly, you'll still have problems with people using @_, caller and return inside the loop body.

This is the nicest I could get things to work using Parse::Keyword (Perl 5.14+ only)...

use v5.14; use strict; use warnings qw(all); BEGIN { package More::For; use Carp; use Exporter::Shiny our @EXPORT = qw(more); use PadWalker (); use Parse::Keyword { more => \&_parse_more }; use Scope::Upper qw( localize UP ); sub _parse_more { my $tmpvar = '@More::For::____list_' . do { state $cnt = 0; ++ +$cnt }; lex_read_space; lex_peek(7) eq 'foreach' ? lex_read(7) : lex_peek(3) eq 'for' ? lex_read(3) : croak("Expected opening parenthesis"); lex_read_space; lex_peek eq '(' ? lex_read(1) : croak("Expected opening parenthesis"); lex_read_space; my $list = parse_fullexpr; lex_read_space; lex_peek eq ')' ? lex_read(1) : croak("Expected closing parenthesis"); lex_read_space; lex_peek(2) eq 'as' ? lex_read(2) : croak("Expected 'as'"); lex_read_space; my $vars = ''; while (lex_peek ne '{') { $vars .= lex_peek(1); lex_read(1); } my $count_vars = split /\,/, $vars; lex_read(1); lex_stuff( sprintf( 'while (%s = splice(%s, 0, %d)) {', $vars, $tmpvar, $count_vars, ), ); return (sub { $tmpvar, $list }, 1); } sub more { my ($tmpvar, $list) = @_; # Fix Parse::Keyword's handling of closed over variables my $peek_my; my %closed_over = %{PadWalker::closed_over($list)}; for my $var (keys %closed_over) { $peek_my //= PadWalker::peek_my(1); $closed_over{$var} = $peek_my->{$var}; } PadWalker::set_closed_over($list, \%closed_over); localize($tmpvar, [$list->()], UP); } }; package main; use More::For; my $x = 1; my $y = 9; more for ( $x .. $y ) as ( my($a), our($b), my $c ) { say join(":", $a, $main::b, $c); } say "done";

last, next, redo, return, caller, and @_ should all work fine in the loop body. Can't say I'm massively fond of the syntax I ended up with - I didn't put that much thought into it. For short lists, it's probably slower than yours because it needs to do more set-up before the loop starts. But for longer lists it should be faster, because once the initialization is done, the loop body has just been rewritten to a plain old while/splice loop.

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

Replies are listed 'Best First'.
Re^2: Silencing specific warnings when executing coderef?
by LanX (Saint) on Mar 23, 2014 at 15:16 UTC
    Thanks Toby!

    You are the man to talk to! :)

    Will you come to Sofia?

    > For what it's worth, even if you can get last to work flawlessly, you'll still have problems with people using @_, caller and return inside the loop body.

    • I think @_ can be easily fixed.

    • Dunno if caller is somehow safely overwritable or if the stack can be manipulated. But even if not, I think it's tolerable for a functional construct not to be too transparent.

    • return is certainly a problem

    > For short lists, it's probably slower than yours because it needs to do more set-up before the loop starts.

    Yep but I prefer the flexibility and back-portability of a pure Perl solution before worrying about speed as long as it doesn't exceed factor 10.

    And I'm still fiddling with B::Deparse.

    And as soon as a functional syntax is tested an accepted, it could easily be ported to XS.

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

    Cheers Rolf

    ( addicted to the Perl Programming Language)

      Dunno if caller is somehow safely overwritable

      Don't join the too-many developers who have made the mistake of trying to replace caller so that they can hide their magic even more. Code behavior should never depend on caller (that is putting the dependency in the wrong order, despite it being a common trick when loading package-impacting modules like Exporter). So caller should be for debugging on some level.

      The last thing I want is to be lied to when I'm trying to debug a problem (a task that is often complicated already). So modules that overload caller are simply unacceptable to me.

      - tye        

        I agree mostly, thats what I meant with "it's tolerable for a functional construct not to be too transparent.". I don't wanna run against windows while running.

        But I might offer such an import option for the case that someone really needs to overwrite it.

        Anyway caller() has after all a clumsy interface (indexed list), something like xcaller() returning a (blessed) hash could handle both cases.

        Cheers Rolf

        ( addicted to the Perl Programming Language)

      "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
        > 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)

Re^2: Silencing specific warnings when executing coderef?
by LanX (Saint) on Mar 24, 2014 at 03:32 UTC
    Are Pluggable Keywords and Parse::Keyword related?

    FWIW I found a pretty simple way to detect if someone tries to use a return statement within a loop and to warn the author. Parsing the Op-tree with B::Concise shows explicit return ops.

    BUT the concept is flawed cause loops can't be nested since @list is shared by all instances.¹ :(

    Cheers Rolf

    ( addicted to the Perl Programming Language)

    update

    ¹) Think I found an easy way to solve this by accessing the input-array via the caller(1)! :)

      Parse::Keyword is a Perl interface to the XS pluggable keyword API.

      Here's how I look for return and wantarray in Sub::Block (a module which might, by the way be useful for your purposes).

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