Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perl function that takes a BLOCK as the second parameter?

I want to write a function whose first parameter is a description, and the second parameter is a code block. I want the finished code to read like:

verify "description" { boolean-assertion-block };

I'm specifically looking to avoid the sub keyword.

I can put the description AFTER the code block, no problem:

sub verify (&$) { ... }

But when I reverse the prototype symbol order:

sub verify ($&) { ... }

Then I get an error message:

Type of arg 2 to main::verify must be sub {} (not anonymous hash ({})) at ...

Clearly, Perl has special handling for the first argument being a code block.

So, maybe I can make it a curried function?

sub verify ($) {
    my $message = shift;
    return sub (&) { . . . }
}

But then I get a syntax error between the description and the code block:

syntax error at ... near ""..." { "

I tried altering the calling syntax to try to help out the compiler:

test "...", { BLOCK };
test("..."){ BLOCK };
test("...")({ BLOCK });
( test "..." )({ BLOCK });

No joy. Can Perl even do what I want to do?

like image 601
John Arrowwood Avatar asked Oct 12 '25 11:10

John Arrowwood


2 Answers

The (&) prototype only has such niceness when used for the first argument in a sub. From perldoc perlsub:

The interesting thing about "&" is that you can generate new syntax with it, provided it's in the initial position

One way to provide a similar level of niceness would be:

sub verify ($%) {
  my ( $message, %opts ) = @_;
  my $coderef = $opts{using};
  ...;
}

sub using (&) {
  my ( $coderef ) = @_;
  return ( using => $coderef );
}

# The `verify` sub accepts a name followed by a hash of options:
#
verify(
  "your message here",
  "using" => sub { ... },
);

# The `using` sub returns a two-element list that will satisfy
# that hash of options:
#
verify "your message here", using {
  ...;
};

If you desperately want to allow a syntax exactly like:

verify "description" { boolean-assertion-block };

... then it is still possible, but requires the dark arts. Keyword::Simple is probably your best bet, but Devel::Declare and Filter::Simple are options.

like image 183
tobyink Avatar answered Oct 14 '25 00:10

tobyink


You can only use the block syntax if the & is the first thing in your prototype. From perlsub:

An & requires an anonymous subroutine, which, if passed as the first argument, does not require the sub keyword or a subsequent comma

Other custom DSL such as in Dancer2 or Mojolicious typically use the sub keyword.

get '/foo' => sub {
  ...
};

Plack::Builder and Web::Scraper use blocks that return objects, which then can be nested.

like image 33
simbabque Avatar answered Oct 14 '25 01:10

simbabque