Skip Menu |

This queue is for tickets about the Scalar-List-Utils CPAN distribution.

Report information
The Basics
Id: 95409
Status: open
Priority: 0/
Queue: Scalar-List-Utils

People
Owner: Nobody in particular
Requestors: roman.daniel [...] davosro.cz
Cc:
AdminCc:

Bug Information
Severity: Normal
Broken in: (no value)
Fixed in: (no value)



Subject: pairmap lexical vars inside coderef
Running pairmap the variables inside anonymous sub "are not created" for each iteration as I would expected. Better with example: use strict; use warnings; use List::Util qw(pairmap); my @subs = pairmap { my $v = "$a,$b"; sub {$v}; } one => 'ONE', two => 'TWO', three => 'THREE'; warn $_->(),"\n" for @subs; This example displays three,THREE three,THREE three,THREE while I would expect one,ONE two,TWO three,THREE Is there any simple explanation for such behaviour (I admit it is probably not a typical usage of pairmap). Similar example with map works as I expect: my @subs = map { my $v = $_; sub {$v}; } 'one','two','three'; warn $_->(),"\n" for @subs; displays one two three
On Tue May 06 12:01:38 2014, DANIELR wrote: Show quoted text
> Running pairmap the variables inside anonymous sub "are not created" > for each iteration as I would expected. Better with example: > > use strict; > use warnings; > use List::Util qw(pairmap); > > my @subs = pairmap { > my $v = "$a,$b"; > sub {$v}; > } > one => 'ONE', two => 'TWO', three => 'THREE'; > > warn $_->(),"\n" for @subs; > > This example displays > > three,THREE > three,THREE > three,THREE
Indeed it does; I can reproduce that here. That is most odd. At first consideration, I suspect a side-effect of the MULTICALL setup used to run the actual code block. map {} being an internal operator that takes a real expression rather just a CODE reference means it doesn't have to rely on MULTICALL to get the same lack-of-overhead around it. -- Paul Evans
As a workaround, I can suggest wrapping the code block in a second set of {} braces, to make a new block that -will- get ENTER/LEAVEd each time the outer one is called: $ perl -MList::Util=pairmap -E 'say $_->() for pairmap { my $v = "$a,$b"; sub {$v} } one => "ONE", two => "TWO", three => "THREE"' three,THREE three,THREE three,THREE $ perl -MList::Util=pairmap -E 'say $_->() for pairmap { { my $v = "$a,$b"; sub {$v} } } one => "ONE", two => "TWO", three => "THREE"' one,ONE two,TWO three,THREE Offhand I'm afraid I can't really think of a good way to have this detected at runtime to disable the MULTICALL optimisation, without effectively having as much overhead to detect it as MULTICALL itself saves. -- Paul Evans
As currently I don't have a good solution to this bug, I will at least document it in the KNOWN BUGS section, along with the double-brace workaround so people will know how to work around it. I think a proper solution may involve looking at the SvREFCNT of every SV in the block's pad. Clearly if every SV has refcount 0 then we're safe to use MULTICALL, but I wonder if that's overkill. A finer-grained solution would be to compare before/after counts and stop doing MULTICALL the moment any refcount gets increased, but it would have to be carefully benchmarked to see if that's actually any quicker than just not bothering with MULTICALL in the first place. -- Paul Evans
On Thu Jun 05 10:22:29 2014, PEVANS wrote: Show quoted text
> As currently I don't have a good solution to this bug, I will at least > document it in the KNOWN BUGS section, along with the double-brace > workaround so people will know how to work around it.
May be there should be noted in KNOWN BUGS section that the problem with lexicals is not limited to a returned closure but probably occurs with any reference to a lexical variable. We were recently got bitten with something like this. use strict; use List::Util qw(pairmap); use Data::Dumper; warn Dumper([ pairmap { my @h = ( $a, $b ); \@h; } a => 1, b => 2, c => 3 ]); which yields $VAR1 = [ [ 'c', 3 ], $VAR1->[0], $VAR1->[0] ]; I do not know anything about Perl internals but is the MULTICALL optimization speedup really worth all the explanations?
On Thu Jun 05 10:22:29 2014, PEVANS wrote: Show quoted text
> As currently I don't have a good solution to this bug, I will at least > document it in the KNOWN BUGS section, along with the double-brace > workaround so people will know how to work around it. >
I guess that also reduce is affected as the function collecting the returned value: use strict; use List::Util qw(reduce); use Data::Dumper; sub myreduce(&@) { my ($code, $result) = (shift(), shift()); while (@_) { ( $a, $b ) = ( $result, shift() ); $result = $code->(); } return $result; } warn Dumper( reduce { my @ary = ( $a, $b ); \@ary; } qw(a b c ) ); warn Dumper( myreduce { my @ary = ( $a, $b ); \@ary; } qw(a b c ) ); which yields $VAR1 = [ $VAR1, 'c' ]; $VAR1 = [ [ 'a', 'b' ], 'c' ];