Perl's syntax for its "functions" that take a block argument is a bit weird, and it's one of my Perl annoyances. There are some things that are just weird because that's how Perl does it:
grep {...} @array; # no comma, block argument
grep $_ == 4, @array # comma, expression argument
Adding the sub
doesn't look like a block argument to Perl simply because that's not the way that Perl parses things:
grep sub { $_ == 4} @array # missing comma, expression argument, compilation error
grep sub { $_ == 4} @array # comma, expression argument
But this works when you use this special block form with the sub
left off, and Perl knows how to parse these special cases because Perl knows how to parse these special cases:
$ perl -MO=Deparse -e 'my @f = grep { $_ == 4 } @array'
my(@f) = grep({$_ == 4;} @array);
-e syntax OK
$ perl -MO=Deparse -e 'my @f = grep $_ == 4, @array'
my(@f) = grep(($_ == 4), @array);
-e syntax OK
$ perl -MO=Deparse -e 'my @f = grep sub{$_ == 4}, @array'
my(@f) = grep(sub {
$_ == 4;
}
, @array);
-e syntax OK
$ perl -MO=Deparse -e 'my @f = grep sub{$_ == 4} @array'
Array found where operator expected at -e line 1, near "} @array"
(Missing operator before @array?)
syntax error at -e line 1, near "} @array
"
-e had compilation errors.
That's just how it is. I wish Perl had a more general idea of anonymous functions, and that's one of the things that Raku addressed. I think Ruby did a nice job with optional blocks, too.
Now, let's make our own function, f
with a block argument by using prototypes (which is more often than not a the best idea). The situation is slightly different (maddening, I know) for a user-defined function than Perl's builtin stuff:
Give f
a block, no problem:
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f {137}'
sub f (&) {
$_[0]->();
}
print f(sub {
137;
}
);
-e syntax OK
Give f
a anonymous sub
, no problem:
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f sub {137}'
sub f (&) {
$_[0]->();
}
print f(sub {
137;
}
);
-e syntax OK
But, use parens and Perl thinks the block is an anonymous hash, even if you try to trick Perl into seeing it as a code block:
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f({137})'
Type of arg 1 to main::f must be block or sub {} (not anonymous hash ({})) at -e line 1, near "})
"
-e had compilation errors.
sub f (&) {
$_[0]->();
}
print &f({137});
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f({137;})'
syntax error at -e line 1, near ";}"
-e had compilation errors.
sub f (&) {
$_[0]->();
}
$ perl -MO=Deparse -e 'sub f (&) { $_[0]->() }; print f({return 137})'
Type of arg 1 to main::f must be block or sub {} (not anonymous hash ({})) at -e line 1, near "})
"
-e had compilation errors.
sub f (&) {
$_[0]->();
}
print &f({(return 137)});
And sometimes that's just the way it is.