I'm playing around with Perl prototypes to learn more about them; I understand that they don't work like most other languages. I don't want them to. I'm specifically looking to get a wrapper function to use a bare block as a code reference, which I think is implied with these two pieces from perlsub
:
Because the intent of this feature is primarily to let you define subroutines that work like built-in functions
which I understand to mean that you can drop the parens, and
An & requires an anonymous subroutine, which, if passed as the first argument, does not require the sub keyword or a subsequent comma.
I also want to pass additional arguments to the wrapper function (not to the coderef). It's all syntactic sugar, but that's what I'm after.
I have the following code that I thought would work:
#!/usr/bin/env perl
use strict;
use warnings;
use 5.014;
sub with_some_context (&;+)
{
my($coderef, $context) = @_;
{
local %ENV = %ENV;
foreach my $key (keys %$context) {
$ENV{$key} = $context->{$key};
}
$coderef->();
}
}
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} => { shift(@ARGV) => 10 };
with the intent that the { shift(@ARGV) => 10 }
hashref is the second argument to the with_some_context
function, i.e. $context
. The compiler complains and does not use the hashref as the second argument:
$ /tmp/foo SHLVL TERM SHLVL LANG
Useless use of anonymous hash ({}) in void context at /tmp/foo line 26.
SHLVL: 1
TERM: xterm-256color
SHLVL: 1
LANG: en_US.UTF-8
If I put in the word sub
, though, the compiler suddenly understands my intent:
with_some_context sub {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} => { shift(@ARGV) => 10 };
as is evidenced when I run it:
$ /tmp/foo SHLVL TERM SHLVL LANG
TERM: xterm-256color
SHLVL: 10
LANG: en_US.UTF-8
If I drop the sub
and put in explicit parens around both arguments:
with_some_context({
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
}, { shift(@ARGV) => 10 });
the compiler completely loses its shit:
$ /tmp/foo SHLVL TERM SHLVL LANG
"my" variable $key masks earlier declaration in same statement at /tmp/foo line 24.
"my" variable %ENV masks earlier declaration in same statement at /tmp/foo line 24.
"my" variable $key masks earlier declaration in same statement at /tmp/foo line 24.
"my" variable @ARGV masks earlier declaration in same statement at /tmp/foo line 26.
syntax error at /tmp/foo line 23, near "foreach "
Execution of /tmp/foo aborted due to compilation errors.
But if I put the sub
back:
with_some_context(sub {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
}, { shift(@ARGV) => 10 });
or drop the comma and put the parens just around the hashref:
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
} ({ shift(@ARGV) => 10 });
it all starts working again:
$ /tmp/foo SHLVL TERM SHLVL LANG
TERM: xterm-256color
SHLVL: 10
LANG: en_US.UTF-8
Running with the sub
, without the parens, and without the hashref:
with_some_context {
foreach my $key (@ARGV) {
say("$key: ", (defined $ENV{$key} ? $ENV{$key} : "<undef>"));
}
};
produces no errors, but also doesn't do anything terribly useful:
$ /tmp/foo SHLVL TERM SHLVL LANG
SHLVL: 1
TERM: xterm-256color
SHLVL: 1
LANG: en_US.UTF-8
Clearly I have no understanding of what's going on, as none of this seems consistent to me. I'm very clearly missing something, but I've no clue what.