How to use a nested closure as the first argument to List::Util::reduce?

Viewed 134

NB: the closure featured in this question is just a convenient example; the one I'm actually working with is substantially more complex than this. IOW, please disregard the details of this closure; all that matters, AFAICT, is that it refers to lexical variables in the parent scope.


I want to redefine the sub foo below so that the first argument in the call to List::Util::reduce is replaced with a reference to a nested closure.

use strict;
use warnings FATAL => 'all';
use List::Util;

sub foo {
  my ( $x, $y ) = @_;
  return List::Util::reduce { $y->[ $b ]{ $x } ? $a + ( 1 << $b ) : $a } 0, 0 .. $#$y;
}

My first attempt was this:

sub foo {
  my ( $x, $y ) = @_;
  sub z {
    our ( $a, $b );
    return exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a;
  }
  return List::Util::reduce \&z, 0, 0 .. $#{ $y };
}

...but this results in a warning saying that Variable "$y" will not stay shared.

I've seen this sort of error before, and the only way I know around it is to replace the nested sub definition with an anonymous sub assigned to a variable. Therefore, I tried this instead:

sub foo {
  my ( $x, $y ) = @_;
  my $z = sub {
    our ( $a, $b );
    return exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a;
  };
  return List::Util::reduce( $z, 0, 0 .. $#{ $y } );
}

Now the error says Type of arg 1 to List::Util::reduce must be block or sub {} (not private variable).

This I really don't understand. Why would passing a subref as the first argument to reduce not be supported?


PS: The following does work:

sub foo {
  my ( $x, $y ) = @_;
  my $z = sub {
    our ( $a, $b );
    return exists $y->[ $b ]{ $x } ? $a | ( 1 << $b ) : $a;
  };
  return List::Util::reduce { $z->( $a, $b ) } 0, 0 .. $#{ $y };
}

...but I really would like to know (a) what's wrong with using a subref as the first argument to List::Util::reduce (e.g. is there some technical impediment to supporting this variant?); and (b) is there a more direct approach (than { $z->( $a, $b ) }) to passing to List::Util::reduce a nested closure?

2 Answers
Related