p5h / p5summit-2019

Perl 5 Summit
0 stars 0 forks source link

The ability to add sublike keywords in the language #1

Open toddr opened 4 years ago

toddr commented 4 years ago

@exodist:

For me the inability to create sublike "keywords" without resorting to Devel::Declare or the undocumented/unclear/unsuable builtin keywords system. I want to be able to define something like this:

sublike 'foo' => sub { my ($name, $paren_string, $coderef) = @_; ... };
...
foo mything(whatever, I, want) { .... } # Note lack of training semicolon

which results in the block for 'foo' being called, at BEGIN-time when the keyword 'foo' is seen where $name contains 'mything', $paren_string contains 'whatever, I want'. and the coderef is the block provided to foo.

Seriously, make it possible to define a "sub-like" keyword, our own keyword that has syntax like 'sub' *syntax/behavior like sub

That means, parsed at begin-time (and keyword callback ran at BEGIN time), get anything put into the parentheses as a string to do with as I please, and a codeblock that does not need a trailing semicolon That is like the holy grail that would make 95% of the Devel::Declare and other keyword magic that nobody can figure out go away, the remainng 5% is probably stuff nobody should be doing anyway

Correction, the "block" should also be returned as a string, and you should also have access to the package, filename, and line number where the block starts. You need that in order to do things like inject variables declared in the parens into the codeblock so, the keyword should get: ($name, $paren_string, block_string, block_package, block_file, block_start_line) = @_

That gives you all you need to write a string eval for the block that includes injected vars you parse from the paren string, and lets you use the #line 42 "filename" to make sure the block is compiled with the correct file+line numbers, heck that directive can be injected into the start of the block string for you it is far from perfect, but it is sufficient

exodist commented 4 years ago

One more clarification, caller() is not able to get the correct line number, as caller returns the LAST line of the statement, not the first of the block. Need the like where the block starts, even if someone does this:

foo mything(...)
{ # This is the line number we need.
...
}
exodist commented 4 years ago

Of note, I have a thing that meets the end-goal of this request, but using Devel::Declare: https://metacpan.org/release/Devel-Declare-Parser

It is not quite the same method, but the end-goal is achieved.

leonerd commented 4 years ago

A curious request. To some extent I suspect a lot of it would be solveable with better docs and explanation around the keyword system. I've been vaguely trying with some blog posts of mine but I'm finding it hard to know what to write about because nobody asks me questions.

I've been thinking about how to make a macro system (a real syntax-aware one, not a silly C-preprocessor style one, don't worry), and a lot of that would solve this too.

Overall I don't see how this request is a core problem though - it would be very easy to create an XS module on CPAN to do it. Suggest me a name and I'll have a stab at it if you really want

exodist commented 4 years ago

Keyword::SubLike

leonerd commented 4 years ago

I now have the tiniest of initial stab implementation, but it passes the following unit-test:

#!/usr/bin/perl

use strict;
use warnings;

use Test::More;

use Keyword::SubLike;

my @results;

BEGIN {
   push @results, "BEGIN";
}

BEGIN {
   sublike foo => sub {
      my ( $name, $parenstring, $code ) = @_;

      push @results, "$name - $parenstring";

      $code->();
   };
}

push @results, "runtime";

foo mything(whatever, I, want) {
   push @results, "code of mything";
}

push @results, "eof";

is_deeply( \@results,
   [ "BEGIN",
     "mything - (whatever, I, want)",
     "code of mything",
     "runtime",
     "eof" ],
   'Results are accumulated correctly' );

done_testing;

This may be sufficient for your purposes - though I think it demonstrates that this feature can in fact easily be provided as an XS module on existing core technology.

exodist commented 4 years ago

@leonerd that's awesome! Where can I find the code? Also, does it provide the necessary line number so a string eval can be written with the code that reports the proper line numbers for statements in that code?

leonerd commented 4 years ago

I'm not quite sure I follow that bit about the string-eval. I can't just pass you the lexical text of the block - most notably because I can't easily parse that out myself - but moreover because it might not make sense out of context (e.g. consider captured lexicals within it):

my $ten = 10;
foo mything() {
    say "ten is $ten";
}

What happens to $ten here?

Perhaps explain what you want to use this thing for, and we can see if there is a better way to implement it. But either way perhaps we can find someplace else to discuss that than hijack what probably isn't any more a p5h issue..?

exodist commented 4 years ago

I want to be able to inject variables into the codeblock based on what I parse out of the paren string. For instance if I wanted to use this to implement a 'method' sub-like keyword that auto-shifts and injects '$self'. Now obviously we already have a few such things on cpan, that is just the first example that comes to mind.

What I was thinking was if the codeblock was a string I could do a string eval on it with the injected variables, but to make sure line numbers were correct I would need to know the initial line number from where the codeblock was grabbed in the file to add the #line 42 special comment.

exodist commented 4 years ago

Thinking about it, the current use case I want does not need to inject any vars, so what you have would actually meet my needs.

As for hijacking, how about you put the code in a new github repo and we can move the discussion there?

exodist commented 4 years ago

My use case would be this: https://metacpan.org/pod/Test2::Plugin::SpecDeclare

Currently it uses some Devel::Declare stuff layered pretty thick, making it hard to figure out why things go wrong.

In this specific use case the stuff inside the parens is parsed as a list. So thinking about it, it would suffer from the same issues as the codeblock when treated as just a string, if someone used a variable in those parens for instance it would break when given the string treatment. The Devel::Declare version does not have that issue as it transforms the code in-place.

leonerd commented 4 years ago

https://github.com/leonerd/Keyword-SubLike