I would love to write: async sub + signatures

((A followup to my message
  Subject: Metathread - Programs I would love to write
))

I would love to write

  use feature 'signatures';
  use Future::AsyncAwait;
  use Future::HTTP;

  async sub get_user_info($uid) {
    my $ua = Future::HTTP->new;
    my $body = await $ua->http_get("https://example.org/api/user/$uid");
    return json_decode($body->cobntent);
  }

This would be nice, as it combines two good features:

  * async/await syntax to neaten up asynchronous code and Future-based
    control flow. It looks a lot neater and more readable than the
    previous style of ->then chaining; a subject on which I have
    written at great length[1].

  * subroutine signatures to neaten up the unpacking of arguments
    passed into functions.

However, currently I cannot write this:

  Expected async sub NAME to be followed by '{' at ...

This is due to internal implementation reasons within
Future::AsyncAwait, but before I get into that I want first to
establish the overall theme for this thread; namely that:

  We believe the ability to combine `async sub` with signatures is a
  useful ability to try to achieve.

If we generally agree it'd be nice to be able to write this sort of
thing, then I'll expand more on why it currently doesn't work, why I
believe p5p are the place to begin solving it, and then we can work on
how to fix it.

(There has previously been some discussion on this issue, but it so far
 hasn't gone anywhere:

  https://rt.perl.org/Public/Bug/Display.html?id=132474
)

-----

[1]: http://leonerds-code.blogspot.com/2019/04/awaiting-future.html

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/
0
leonerd
7/3/2019 11:35:59 AM
perl.perl5.porters 47815 articles. 1 followers. Follow

5 Replies
51 Views

Similar Articles

[PageSpeed] 18

On Wed, Jul 03, 2019 at 12:35:59PM +0100, Paul "LeoNerd" Evans wrote:
>   We believe the ability to combine `async sub` with signatures is a
>   useful ability to try to achieve.

Are you suggesting an extension to the existing prototype feature that
allows you to pass anonymous subroutine as an argument to a function
without needing to include the 'sub' keyword, e.g.

    sub foo($@) { ... }
    foo { $_*2 } @args;

if so, is your suggestion roughly equivalent in meaning to:

    sub foo($@) { ... }
    sub per_item($item) { $item*2 }
    foo \&per_item @args;

If not, what is it equivalent to?


-- 
A power surge on the Bridge is rapidly and correctly diagnosed as a faulty
capacitor by the highly-trained and competent engineering staff.
    -- Things That Never Happen in "Star Trek" #9
0
davem
7/4/2019 7:33:54 AM
On Thu, 4 Jul 2019 08:33:54 +0100
Dave Mitchell <davem@iabyn.com> wrote:

> On Wed, Jul 03, 2019 at 12:35:59PM +0100, Paul "LeoNerd" Evans wrote:
> >   We believe the ability to combine `async sub` with signatures is a
> >   useful ability to try to achieve.  
> 
> Are you suggesting an extension to the existing prototype feature that
> allows you to pass anonymous subroutine as an argument to a function
> without needing to include the 'sub' keyword, e.g.
> 
>     sub foo($@) { ... }
>     foo { $_*2 } @args;
> 
> if so, is your suggestion roughly equivalent in meaning to:
> 
>     sub foo($@) { ... }
>     sub per_item($item) { $item*2 }
>     foo \&per_item @args;
> 
> If not, what is it equivalent to?

Ohright; mm I should explain more the syntax.

`async sub` is like a regular `sub` definition, except that its return
value is always a Future, and inside the function it allows use of the
`await` expression. `async sub`s can be named or anonymous:

  async sub foo { ... }
  my $bar = async sub { ... };

((When trying to initially create Future::AsyncAwait, I did at first try
  using an attribute but that made it even more difficult to implement:

    sub foo :async { ... }

  so I gave up on that idea))

The current issue is that an `async sub` can't use signatures, so you
have to use the "old-fashioned" way of unpacking @_:

  async sub get_user {
    my ($uid, %opts) = @_;
    ...
  }

What you can't currently write, which is the thrust of this thread, is

  use feature 'signatures';

  async sub get_user($uid, %opts) {
    ...
  }

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/
0
leonerd
7/4/2019 12:40:27 PM
On Wed, 3 Jul 2019 12:35:59 +0100
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> This is due to internal implementation reasons within
> Future::AsyncAwait, but before I get into that I want first to
> establish the overall theme for this thread; namely that:
> 
>   We believe the ability to combine `async sub` with signatures is a
>   useful ability to try to achieve.
> 
> If we generally agree it'd be nice to be able to write this sort of
> thing, then I'll expand more on why it currently doesn't work, why I
> believe p5p are the place to begin solving it, and then we can work on
> how to fix it.

OK. Well, nobody has particularly objected to this principle, so I
shall imagine we're all roughly onboard with the overall idea.

The reason I believe this feature is currently impossible to build
without p5p's help, has to do with the way that `async sub` is parsed.

How the Future::AsyncAwait parser works is that it catches the `async`
keyword, which is used to introduce a new `async sub`. It can't just
dispatch down to perl's regular parsing at this point and invoke
`parse_fullstmt`, for a couple of reasons:

  *) I need to mangle the optree of the compiled function

  *) I need to know the PL_compcv during compilation for when I later
     encounter an `await` expression

In more detail: the parser has to implement the logic itself, mostly by
copy-and-paste'ing various bits of core internals and other places. It
takes the following steps:

  1) consumes the `sub` keyword

  2) optionally consumes the name as an identifier; a missing one means
     this is an anonymous sub

  3) optionally consumes an attribute list, by parsing the strings
     itself

  4) starts a sub parse context using `start_subparse()`

  5) stores the value of PL_compcv into the hints hash, as the parser
     for `await` will need it

  6) parses the actual block of the function body by using
     `block_start()`, `parse_block()` and `block_end()`

  7) assembles the various pieces together by using `newATTRSUB()`

These steps can all be seen in the function `async_keyword_plugin()`
which is currently found at

  https://metacpan.org/source/PEVANS/Future-AsyncAwait-0.30/lib/Future/AsyncAwait.xs#L2048

The reason this becomes problematic for subroutine signatures is that I
am not aware of any API functions I can invoke at any step of the
process, to a) parse, and b) generate a suitable optree fragment, for
doing so.

So, on to my actual question:

  Can p5p suggest any other way to implement my parser, such that it
  remains working as-is but now also parses signatures?

  Suspecting the answer "no"; can instead p5p suggest a set of API
  functions that I can use to parse out and implement these signatures?

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/
0
leonerd
7/10/2019 3:41:55 PM
On Wed, 10 Jul 2019 16:41:55 +0100
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

>   Suspecting the answer "no"; can instead p5p suggest a set of API
>   functions that I can use to parse out and implement these
>   signatures?

Current progress: I have a suggested set of changes required to
implement this
 ((though it is currently waiting on some collateral change to have
   regen_perly.pl happy with the bison 3.2 that debian now ships with.))

The core of the change adds a new function:

  OP *parse_subsignature(U32 flags);

which returns the optree fragment by parsing the contents of the parens
for a sub signature. This has to be performed after start_subparse(),
i.e. so PL_compcv and friends are set, and will return an optree
fragment which should be prepended onto the body of the function
itself.

Using this function I have been able to parse and implement

  async sub add($x, $y) { return $x + $y }

using a suitably patched Future-AsyncAwait.

So I think this feels right :)

Patch pending - will be in a new thread when it arrives.

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/
0
leonerd
7/11/2019 5:41:08 PM
On Wed, 3 Jul 2019 12:35:59 +0100
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> I would love to write
> 
>   use feature 'signatures';
>   use Future::AsyncAwait;
>   use Future::HTTP;
> 
>   async sub get_user_info($uid) {
>     my $ua = Future::HTTP->new;
>     my $body = await $ua->http_get("https://example.org/api/user/$uid");
>     return json_decode($body->cobntent);
>   }

I'd like to add as a followup for posterity, that this is now possible.

The `parse_subsignature()` API function was added to core perl, and
this allowed Future::AsyncAwait to call it to get the signature parsed
and generate the optree fragment for it. This was added to
Future-AsyncAwait version 0.31, released 2019-07-25.

Thanks all who assisted in making this possible :)

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/
0
leonerd
10/15/2019 12:05:08 AM
Reply: