Mailing List Archive

RFC 0004 - defer {} syntax
Here's an RFC formalization of my currently-open github feature issue:

https://github.com/Perl/perl5/issues/17949

which was itself an issue tracking the previous mailing list
conversation:

https://www.nntp.perl.org/group/perl.perl5.porters/2020/06/msg257611.html

If we accept this one into the RFC process, I'll likely close the
github issue and point at the RFC instead.

---

# defer {} syntax

## Preämble

Author: Paul Evans <PEVANS>
Sponsor:
ID: 0004
Status: Draft

## Abstract

Add a new control-flow syntax written as `defer { BLOCK }` which
enqueues a block of code for later execution, when control leaves its
surrounding scope for whatever reason.

## Motivation

Sometimes a piece of code performs some sort of "setup" operation, that
requires a corresponding "teardown" to happen at the end of its task.
Control flow such as exception throwing, or loop controls, means that
it cannot always be written in a simple style such as

{
setup();
operation();
teardown();
}

as in this case, if the `operation` function throws an exception the
teardown does not take place. Traditional solutions to this often rely
on allocating a lexical variable and storing an instance of some object
type with a `DESTROY` method, so this runs the required code as a result
of object destruction caused by variable cleanup. There are no in-core
modules to automate this process, but the CPAN module `Scope::Guard` is
among the more popular choices.

It would be nice to offer a native core syntax for this common
behaviour. A simple `defer { BLOCK }` syntax removes from the user any
requirement to think about storing the guard object in a lexical
variable, or worry about making sure it really does get released at the
right time.

As well as being more convenient for users, it would help decouple Perl
core's object behaviours, like `DESTROY`, from this task. If less code
relies on DESTROY, it may one day be possible to change the way that
part of the code works, without worrying so much about code that relies
on it breaking.

This syntax has already been implemented as a CPAN module,
[`Syntax::Keyword::Defer`](https://metacpan.org/pod/Syntax::Keyword::Defer).
This RFC formalizes an attempt implement the same in core.

## Rationale

((TODO - I admit I'm not very clear on how to split my wording between
the Motivation section, and this one))

The name "defer" comes from a collection of other languages.
Near-identical syntax is provided by Swift, Zig, Jai, Nim and Odin. Go
does define a `defer` keyword that operates on a single statement,
though its version defers until the end of the containing function, not
just a single lexical block. I did consider this difference, but ended
up deciding that a purely-lexical scoped nature is cleaner and more
"Perlish", overriding the concerns that it differs from Go.

## Specification

A new lexically-scoped feature bit, requested as

use feature 'defer';

enables new syntax, spelled as

defer { BLOCK }

This syntax stands alone as a full statement; much as e.g. a `while`
loop does. The deferred block may contain one or multiple statements.

When the `defer` statement is encountered during regular code
execution, nothing immediately happens. The contents of the block are
stored by the perl interpreter, enqueued to be invoked at some later
time, when control exits the block this `defer` statement is contained
within.

If multiple `defer` statements appear within the same block, the are
eventually executed in LIFO order; that is, the most recently
encountered is the first one to run:

{
setup1();
defer { say "This runs second"; teardown1(); }

setup2();
defer { say "This runs first"; teardown2(); }
}

`defer` statements are only "activated" if the flow of control actually
encounters the line they appear on (as compared to `END` phaser blocks
which are activated the moment they have been parsed by the compiler).
If the `defer` statement is never reached, then its deferred code will
not be invoked:

while(1) {
defer { say "This will happen"; }
last;
defer { say "This will *NOT* happen"; }
}

((TODO: It is currently not explicitly documented, but naturally falls
out of the current implementation of both the CPAN and the in-core
versions, that the same LIFO stack that implements `defer` also
implements other things such as `local` modifications; for example:

our $var = 1;
{
defer { say "This prints 1: $var" }

local $var = 2;
defer { say "This prints 2: $var" }
}

I have no strong thoughts yet on whether to specify and document -
and thus guarantee - this coïncidence, or leave it unsaid.))

## Backwards Compatibility

The new syntax `defer { BLOCK }` is guarded by a lexical feature guard.
A static analyser that is not aware of that feature guard would get
confused into thinking this is indirect call syntax; though this is no
worse than basically any other feature-guarded keyword that controls a
block (e.g. `try/catch`).

For easy compatibility on older Perl versions, the CPAN implementation
already mentioned above can be used. If this RFC succeeds at adding it
as core syntax, a `Feature::Compat::Defer` module can be created for it
in the same style as
[`Feature::Compat::Try`](https://metacpan.org/pod/Feature::Compat::Try).

## Security Implications

None foreseen.

## Examples

A couple of small code examples are quoted above. Further, from the
docs of `Syntax::Keyword::Defer`:

use feature 'defer';

{
my $dbh = DBI->connect( ... ) or die "Cannot connect";
defer { $dbh->disconnect; }

my $sth = $dbh->prepare( ... ) or die "Cannot prepare";
defer { $sth->finish; }

...
}

## Prototype Implementation

CPAN module `Syntax::Keyword::Defer` as already mentioned.

In addition, I have a mostly-complete branch of bleadperl (somewhat
behind since I haven't updated it for the 5.34 release yet) at

https://github.com/leonerd/perl5/tree/defer

I'm not happy with the way I implemented it yet (don't look at how I
abused an SVOP to store the deferred optree) - it needs much tidying up
and fixing for the various things I learned while writing the CPAN
module version.

## Future Scope

If this RFC becomes implemented, it naturally follows to enquire
whether the same mechanism that powers it could be used to add a
`finally` clause to the `try/catch` syntax added in Perl 5.34. This
remains an open question: while it doesn't any new ability, is the
added expressive power and familiarity some users will have enough to
justify there now being two ways to write the same thing?

## Rejected Ideas

On the subject of naming, this was originally called `LEAVE {}`, which
was rejected because its semantics don't match the Raku language
feature of the same name. It was then renamed to `FINALLY {}` where it
was implemented on CPAN. This has been rejected too on grounds that
it's too similar to the proposed `try/catch/finally`, and it shouldn't
be a SHOUTY PHASER BLOCK. All the SHOUTY PHASER BLOCKS are declarations,
activated by their mere presence at compiletime, whereas `defer {}` is
a statement which only takes effect if dynamic execution actually
encounters it. The p5p mailing list and the CPAN module's RT queue both
contain various other rejected naming ideas, such as UNWIND, UNSTACK,
CLEANUP, to name three.

Another rejected idea is that of conditional enqueue:

defer if (EXPR) { BLOCK }

as this adds quite a bit of complication to the grammar, for little
benefit. As currently the grammar requires a brace-delimited block to
immediately follow the `defer` keyword, it is possible that other ideas
- such as this one - could be considered at a later date however.

## Open Issues

Design-wise I don't feel there are any remaining unresolved questions.

Implementation-wise the code still requires some work to finish it off;
it is not yet in a merge-ready state.

## Copyright

Copyright (C) 2021, Paul Evans.
This document and code and documentation within it may be used,
redistributed and/or modified under the same terms as Perl itself.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
:Here's an RFC formalization of my currently-open github feature issue:
:
: https://github.com/Perl/perl5/issues/17949
:
:which was itself an issue tracking the previous mailing list
:conversation:
:
: https://www.nntp.perl.org/group/perl.perl5.porters/2020/06/msg257611.html
[...]

:As well as being more convenient for users, it would help decouple Perl
:core's object behaviours, like `DESTROY`, from this task. If less code
:relies on DESTROY, it may one day be possible to change the way that
:part of the code works, without worrying so much about code that relies
:on it breaking.

At least the second sentence here feels like a red herring. I'd be very
happy to see defer in the language, but I cannot envisage its existence
significantly reducing the need to retain DESTROY behaviour within any
relevant timescale.

:Another rejected idea is that of conditional enqueue:
:
: defer if (EXPR) { BLOCK }

Given the (snipped) reference to symmetry with the behaviour of local,
I would be hopeful that you could get the same effect with:
defer BLOCK if EXPR;
just as we can get conditional localization (something of which I make
heavy use in recursive code), eg:
local @hash{@keys} = @values if $localize;

Hugo
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Wed, Jun 16, 2021 at 7:21 PM <hv@crypt.org> wrote:

> :Another rejected idea is that of conditional enqueue:
> :
> : defer if (EXPR) { BLOCK }
>
> Given the (snipped) reference to symmetry with the behaviour of local,
> I would be hopeful that you could get the same effect with:
> defer BLOCK if EXPR;
> just as we can get conditional localization (something of which I make
> heavy use in recursive code), eg:
> local @hash{@keys} = @values if $localize;
>

I had a similar thought - but this would necessitate making `defer BLOCK`
an expression rather than a complete statement, so that it could be used
with statement modifiers similarly to `do BLOCK`; this means that bare
`defer BLOCK` will need a semicolon. It seems useful for if and unless
conditions, but haven't really thought it through.

-Dan
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Wed, 16 Jun 2021 23:47:27 +0100
hv@crypt.org wrote:

> "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
> :Here's an RFC formalization of my currently-open github feature
> issue: :
> : https://github.com/Perl/perl5/issues/17949
> :
> :which was itself an issue tracking the previous mailing list
> :conversation:
> :
> :
> https://www.nntp.perl.org/group/perl.perl5.porters/2020/06/msg257611.html
> [...]
>
> :As well as being more convenient for users, it would help decouple
> Perl :core's object behaviours, like `DESTROY`, from this task. If
> less code :relies on DESTROY, it may one day be possible to change
> the way that :part of the code works, without worrying so much about
> code that relies :on it breaking.
>
> At least the second sentence here feels like a red herring. I'd be
> very happy to see defer in the language, but I cannot envisage its
> existence significantly reducing the need to retain DESTROY behaviour
> within any relevant timescale.

Fair - I guess I could just delete that bit. It's a _far_ off potential
idea anyway.

> :Another rejected idea is that of conditional enqueue:
> :
> : defer if (EXPR) { BLOCK }
>
> Given the (snipped) reference to symmetry with the behaviour of local,
> I would be hopeful that you could get the same effect with:
> defer BLOCK if EXPR;
> just as we can get conditional localization (something of which I make
> heavy use in recursive code), eg:
> local @hash{@keys} = @values if $localize;

defer { BLOCK }
^-- the parser has now stopped looking for a complete
statement


defer { BLOCK } if EXPR;
^-- oops - parser wanted to find a `(` because it was
expecting if(EXPR) { BLOCK }


--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
2021-6-17 7:16 Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:

> Here's an RFC formalization of my currently-open github feature issue:
>
> https://github.com/Perl/perl5/issues/17949
>
>
Personally, I think defer block syntax is difficult to implement.

Especially the relation of

1. GC
2. Destructor

The relation of GG and block of Current Perl is simple. an object is
released at the end of the block if the object reference count is 0.

defer syntax maybe add complex things of this relation.

This feature seems to be planned for the finally block of try catch syntax.

Are there other strong benefits?
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 at 07:15, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

> The name "defer" comes from a collection of other languages.
> Near-identical syntax is provided by Swift, Zig, Jai, Nim and Odin. Go
> does define a `defer` keyword that operates on a single statement,
> though its version defers until the end of the containing function, not
> just a single lexical block.


"defer" in Go is used to catch ("recover") exceptions ("panic") which
covers "very bad" things like memory errors (segmentation fault, etc.) so I
think it has to be at the "end of the function" since it is basically being
called as you go back down the stack of functions when recovering from a
"disaster" in a called routine. For example if you write an http server in
Go and the function you pass to make the web page crashes, the http server
doesn't stop functioning but just prints a message and continues.

That requirement doesn't seem to apply here.


> I did consider this difference, but ended
> up deciding that a purely-lexical scoped nature is cleaner and more
> "Perlish", overriding the concerns that it differs from Go.
>

I can imagine cases where you might want to clean up after each go around
of a loop. But I find it quite hard to imagine how it would be useful to
have a function which runs at the end of an "if" statement block regardless
of what else happens.

Would it be useful to make the "defer" statement within an "if" block refer
to the enclosing function/loop/other block rather than the "if" block, in
the same way that a "next" statement within the if block refers to the
enclosing loop?
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 at 08:37, Ben Bullock <benkasminbullock@gmail.com>
wrote:

> I can imagine cases where you might want to clean up after each go around
> of a loop. But I find it quite hard to imagine how it would be useful to
> have a function which runs at the end of an "if" statement block regardless
> of what else happens.
>

As a generic example:

if($resource->acquire) {
defer { $resource->release; }
... return, exception, next, or similar action here ...
}.
# This line is never reached, so the ->release doesn't happen
# $resource->release;


Would it be useful to make the "defer" statement within an "if" block refer
> to the enclosing function/loop/other block rather than the "if" block, in
> the same way that a "next" statement within the if block refers to the
> enclosing loop?
>

This would introduce more complexity to the mental model - it's scoped to
the block unless it's an `if/else/elsif/unless` block, but unlike next/last
you can't override the scope with a label - and would take it further away
from the behaviour currently provided by Scope::Guard or
B::Hooks::EndOfScope.

Do you have any examples in mind where this would be a better fit than the
scoped-to-block implementation currently described, though?
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 at 09:50, Tom Molesworth <tom@binary.com> wrote:

> On Thu, 17 Jun 2021 at 08:37, Ben Bullock <benkasminbullock@gmail.com>
> wrote:
>
>> I can imagine cases where you might want to clean up after each go around
>> of a loop. But I find it quite hard to imagine how it would be useful to
>> have a function which runs at the end of an "if" statement block regardless
>> of what else happens.
>>
>
> As a generic example:
>
> if($resource->acquire) {
> defer { $resource->release; }
> ... return, exception, next, or similar action here ...
> }.
> # This line is never reached, so the ->release doesn't happen
> # $resource->release;
>

To clarify, in terms of your example, my query is about this:

sub superduper
{
if ($resource->acquire) {
defer { $resource->release }
if ($resource->failure) {
# Case failure: Either way it runs here
return undef;
}
if (0 == -1) {
# Case exception: Either way it runs here
die "Oh no I have failed";
}
if ($resource->blocks) {
# Case next: Either way it runs here.
next;
}
# Case if block finishes: At the moment it runs here <-----------
why does it run here?
}
# Case enclosing loop finishes: <------------- Why not run here instead?
return 1;
}

> Do you have any examples in mind where this would be a better fit than
the scoped-to-block implementation currently described, though?

The "defer" is called within the "if" statement then runs at the end of the
if statement, I'm wondering why it can't run at the end of the loop
instead. Then we can do things like this:

while (1) {
if ($resource->too_busy) {
defer {
$resource->close_some_connections;
}
# Don't want to clean up yet.
}
$resource->do_something;
$resource->do_another_thing;
# Clean up here with the defer statements.
}

The reason I'm saying it is because I can't see a big advantage from
running at the end of an if block, but I can see an advantage from being
able to run defers conditionally.
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 10:44:37 +0900, Ben Bullock
<benkasminbullock@gmail.com> wrote:

> On Thu, 17 Jun 2021 at 09:50, Tom Molesworth <tom@binary.com> wrote:
>
> > On Thu, 17 Jun 2021 at 08:37, Ben Bullock
> > <benkasminbullock@gmail.com> wrote:
> >
> >> I can imagine cases where you might want to clean up after each go
> >> around of a loop. But I find it quite hard to imagine how it would
> >> be useful to have a function which runs at the end of an "if"
> >> statement block regardless of what else happens.
> >>
> >
> > As a generic example:
> >
> > if($resource->acquire) {
> > defer { $resource->release; }
> > ... return, exception, next, or similar action here ...
> > }.
> > # This line is never reached, so the ->release doesn't happen
> > # $resource->release;
>
> To clarify, in terms of your example, my query is about this:
>
> sub superduper {
> if ($resource->acquire) {
> defer { $resource->release }
> if ($resource->failure) {
> # Case failure: Either way it runs here
> return undef;
> }
> if (0 == -1) {
> # Case exception: Either way it runs here
> die "Oh no I have failed";
> }
> if ($resource->blocks) {
> # Case next: Either way it runs here.
> next;

Exiting subroutine via next at -e line N
Did you meant return here?

> }
> # Case if block finishes: At the moment it runs here <----------- # why does it run here?
> }
> # Case enclosing loop finishes: <------------- Why not run here instead?
> return 1;
> }
>
> > Do you have any examples in mind where this would be a better fit
> > than the scoped-to-block implementation currently described,
> > though?
>
> The "defer" is called within the "if" statement then runs at the end
> of the if statement, I'm wondering why it can't run at the end of the
> loop instead. Then we can do things like this:
>
> while (1) {
> if ($resource->too_busy) {
> defer {
> $resource->close_some_connections;
> }
> # Don't want to clean up yet.
> }
> $resource->do_something;
> $resource->do_another_thing;
> # Clean up here with the defer statements.
> }
>
> The reason I'm saying it is because I can't see a big advantage from
> running at the end of an if block, but I can see an advantage from
> being able to run defers conditionally.

--
H.Merijn Brand https://tux.nl Perl Monger http://amsterdam.pm.org/
using perl5.00307 .. 5.33 porting perl5 on HP-UX, AIX, and Linux
https://tux.nl/email.html http://qa.perl.org https://www.test-smoke.org
Re: RFC 0004 - defer {} syntax [ In reply to ]
From the keyboard of Ben Bullock [17.06.21,10:44]:

[...]
> To clarify, in terms of your example, my query is about this:
>
> sub superduper 
> {
>    if ($resource->acquire) {
>        defer { $resource->release }
>        if ($resource->failure) {
>              # Case failure: Either way it runs here
>              return undef;
>         }
>         if (0 == -1) {
>              # Case exception: Either way it runs here
>              die "Oh no I have failed";
>          }
>          if ($resource->blocks) {
>               # Case next: Either way it runs here.
>               next;
>           }
>        # Case if block finishes: At the moment it runs here <----------- why does it run
> here?
>    }
>    # Case enclosing loop finishes: <------------- Why not run here instead?
>    return 1;
> }
>
> > Do you have any examples in mind where this would be a better fit than the
> scoped-to-block implementation currently described, though?
>
> The "defer" is called within the "if" statement then runs at the end of the if statement,
> I'm wondering why it can't run at the end of the loop instead. Then we can do things like
> this:
>
> while (1) {
>    if ($resource->too_busy) {
>         defer {
>              $resource->close_some_connections;
>         }
>         # Don't want to clean up yet.
>     }
>     $resource->do_something;
>     $resource->do_another_thing;
>     # Clean up here with the defer statements.
> }
>
> The reason I'm saying it is because I can't see a big advantage from running at the end of
> an if block, but I can see an advantage from being able to run defers conditionally.
>
>

Maybe "defer" should follow the semantics of "next" and "last", which would
allow to say "defer BLOCK" and "defer LABEL BLOCK", with a special label or
label modifier named ":outer" (or ":after" ?).

sub foo {
my $lim = shift;
my (@l, $c);
QWURX: for my $x (1..5) {
defer { say "candidate $x" };
return if $x > $lim;
my $y = $x << 1;
if ($x % 2) {
defer :outer { say "y = $y" }; #2, same as defer QWURX
$c++;
defer :outer QWURX { say "$c. even number" }; #3 also: defer QWURX :outer { }
say "x % 2";
}
say "x = $x";
# defer block #2 runs here
# defer block #1 runs here
}
# defer block(s) #3 run(s) here
}

So, a deferred block can be hooked to a labelled block (or a bare label) at its
end or outside of it. Maybe the modfier ":return" would be useful, to hook the
deferred blocks to the end of or return from a subroutine.

0--gg-

--
_($_=" "x(1<<5)."?\n".q·/)Oo. G°\ /
/\_¯/(q /
---------------------------- \__(m.====·.(_("always off the crowd"))."·
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: RFC 0004 - defer {} syntax [ In reply to ]
There's already a set of modules on CPAN
(https://metacpan.org/pod/Guard,
https://metacpan.org/pod/Scope::Guard, others) doing the same thing
using a scope_guard/guard verb. So, I have a number of questions:

- why invent a new verb for already existing semantic?
- what do all current CPAN implementations lack, thus requiring this feature?
- why has this to be a core feature, compared to yet another CPAN module?
- why any existing module can't be bundled with Perl, achieving this
functionality?

Best regards,
Sergey Aleynikov

??, 17 ???. 2021 ?. ? 01:15, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>:
>
> Here's an RFC formalization of my currently-open github feature issue:
>
> https://github.com/Perl/perl5/issues/17949
>
> which was itself an issue tracking the previous mailing list
> conversation:
>
> https://www.nntp.perl.org/group/perl.perl5.porters/2020/06/msg257611.html
>
> If we accept this one into the RFC process, I'll likely close the
> github issue and point at the RFC instead.
>
> ---
>
> # defer {} syntax
>
> ## Preämble
>
> Author: Paul Evans <PEVANS>
> Sponsor:
> ID: 0004
> Status: Draft
>
> ## Abstract
>
> Add a new control-flow syntax written as `defer { BLOCK }` which
> enqueues a block of code for later execution, when control leaves its
> surrounding scope for whatever reason.
>
> ## Motivation
>
> Sometimes a piece of code performs some sort of "setup" operation, that
> requires a corresponding "teardown" to happen at the end of its task.
> Control flow such as exception throwing, or loop controls, means that
> it cannot always be written in a simple style such as
>
> {
> setup();
> operation();
> teardown();
> }
>
> as in this case, if the `operation` function throws an exception the
> teardown does not take place. Traditional solutions to this often rely
> on allocating a lexical variable and storing an instance of some object
> type with a `DESTROY` method, so this runs the required code as a result
> of object destruction caused by variable cleanup. There are no in-core
> modules to automate this process, but the CPAN module `Scope::Guard` is
> among the more popular choices.
>
> It would be nice to offer a native core syntax for this common
> behaviour. A simple `defer { BLOCK }` syntax removes from the user any
> requirement to think about storing the guard object in a lexical
> variable, or worry about making sure it really does get released at the
> right time.
>
> As well as being more convenient for users, it would help decouple Perl
> core's object behaviours, like `DESTROY`, from this task. If less code
> relies on DESTROY, it may one day be possible to change the way that
> part of the code works, without worrying so much about code that relies
> on it breaking.
>
> This syntax has already been implemented as a CPAN module,
> [`Syntax::Keyword::Defer`](https://metacpan.org/pod/Syntax::Keyword::Defer).
> This RFC formalizes an attempt implement the same in core.
>
> ## Rationale
>
> ((TODO - I admit I'm not very clear on how to split my wording between
> the Motivation section, and this one))
>
> The name "defer" comes from a collection of other languages.
> Near-identical syntax is provided by Swift, Zig, Jai, Nim and Odin. Go
> does define a `defer` keyword that operates on a single statement,
> though its version defers until the end of the containing function, not
> just a single lexical block. I did consider this difference, but ended
> up deciding that a purely-lexical scoped nature is cleaner and more
> "Perlish", overriding the concerns that it differs from Go.
>
> ## Specification
>
> A new lexically-scoped feature bit, requested as
>
> use feature 'defer';
>
> enables new syntax, spelled as
>
> defer { BLOCK }
>
> This syntax stands alone as a full statement; much as e.g. a `while`
> loop does. The deferred block may contain one or multiple statements.
>
> When the `defer` statement is encountered during regular code
> execution, nothing immediately happens. The contents of the block are
> stored by the perl interpreter, enqueued to be invoked at some later
> time, when control exits the block this `defer` statement is contained
> within.
>
> If multiple `defer` statements appear within the same block, the are
> eventually executed in LIFO order; that is, the most recently
> encountered is the first one to run:
>
> {
> setup1();
> defer { say "This runs second"; teardown1(); }
>
> setup2();
> defer { say "This runs first"; teardown2(); }
> }
>
> `defer` statements are only "activated" if the flow of control actually
> encounters the line they appear on (as compared to `END` phaser blocks
> which are activated the moment they have been parsed by the compiler).
> If the `defer` statement is never reached, then its deferred code will
> not be invoked:
>
> while(1) {
> defer { say "This will happen"; }
> last;
> defer { say "This will *NOT* happen"; }
> }
>
> ((TODO: It is currently not explicitly documented, but naturally falls
> out of the current implementation of both the CPAN and the in-core
> versions, that the same LIFO stack that implements `defer` also
> implements other things such as `local` modifications; for example:
>
> our $var = 1;
> {
> defer { say "This prints 1: $var" }
>
> local $var = 2;
> defer { say "This prints 2: $var" }
> }
>
> I have no strong thoughts yet on whether to specify and document -
> and thus guarantee - this coïncidence, or leave it unsaid.))
>
> ## Backwards Compatibility
>
> The new syntax `defer { BLOCK }` is guarded by a lexical feature guard.
> A static analyser that is not aware of that feature guard would get
> confused into thinking this is indirect call syntax; though this is no
> worse than basically any other feature-guarded keyword that controls a
> block (e.g. `try/catch`).
>
> For easy compatibility on older Perl versions, the CPAN implementation
> already mentioned above can be used. If this RFC succeeds at adding it
> as core syntax, a `Feature::Compat::Defer` module can be created for it
> in the same style as
> [`Feature::Compat::Try`](https://metacpan.org/pod/Feature::Compat::Try).
>
> ## Security Implications
>
> None foreseen.
>
> ## Examples
>
> A couple of small code examples are quoted above. Further, from the
> docs of `Syntax::Keyword::Defer`:
>
> use feature 'defer';
>
> {
> my $dbh = DBI->connect( ... ) or die "Cannot connect";
> defer { $dbh->disconnect; }
>
> my $sth = $dbh->prepare( ... ) or die "Cannot prepare";
> defer { $sth->finish; }
>
> ...
> }
>
> ## Prototype Implementation
>
> CPAN module `Syntax::Keyword::Defer` as already mentioned.
>
> In addition, I have a mostly-complete branch of bleadperl (somewhat
> behind since I haven't updated it for the 5.34 release yet) at
>
> https://github.com/leonerd/perl5/tree/defer
>
> I'm not happy with the way I implemented it yet (don't look at how I
> abused an SVOP to store the deferred optree) - it needs much tidying up
> and fixing for the various things I learned while writing the CPAN
> module version.
>
> ## Future Scope
>
> If this RFC becomes implemented, it naturally follows to enquire
> whether the same mechanism that powers it could be used to add a
> `finally` clause to the `try/catch` syntax added in Perl 5.34. This
> remains an open question: while it doesn't any new ability, is the
> added expressive power and familiarity some users will have enough to
> justify there now being two ways to write the same thing?
>
> ## Rejected Ideas
>
> On the subject of naming, this was originally called `LEAVE {}`, which
> was rejected because its semantics don't match the Raku language
> feature of the same name. It was then renamed to `FINALLY {}` where it
> was implemented on CPAN. This has been rejected too on grounds that
> it's too similar to the proposed `try/catch/finally`, and it shouldn't
> be a SHOUTY PHASER BLOCK. All the SHOUTY PHASER BLOCKS are declarations,
> activated by their mere presence at compiletime, whereas `defer {}` is
> a statement which only takes effect if dynamic execution actually
> encounters it. The p5p mailing list and the CPAN module's RT queue both
> contain various other rejected naming ideas, such as UNWIND, UNSTACK,
> CLEANUP, to name three.
>
> Another rejected idea is that of conditional enqueue:
>
> defer if (EXPR) { BLOCK }
>
> as this adds quite a bit of complication to the grammar, for little
> benefit. As currently the grammar requires a brace-delimited block to
> immediately follow the `defer` keyword, it is possible that other ideas
> - such as this one - could be considered at a later date however.
>
> ## Open Issues
>
> Design-wise I don't feel there are any remaining unresolved questions.
>
> Implementation-wise the code still requires some work to finish it off;
> it is not yet in a merge-ready state.
>
> ## Copyright
>
> Copyright (C) 2021, Paul Evans.
> This document and code and documentation within it may be used,
> redistributed and/or modified under the same terms as Perl itself.
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 08:57:20 +0900
Yuki Kimoto <kimoto.yuki@gmail.com> wrote:

> Personally, I think defer block syntax is difficult to implement.

Hm? I've 95% finished implementing it. It was easy.

> Especially the relation of
>
> 1. GC
> 2. Destructor
>
> The relation of GG and block of Current Perl is simple. an object is
> released at the end of the block if the object reference count is 0.
>
> defer syntax maybe add complex things of this relation.
>
> This feature seems to be planned for the finally block of try catch
> syntax.

Indeed; I can reüse about 90% of the implementation of `defer` to add a
`try/catch/finally`.

> Are there other strong benefits?

Mostly those.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
Ben Bullock <benkasminbullock@gmail.com> wrote:
>
> I can see an advantage from being able to run defers conditionally.

consider:

defer if EXPR { ... }
vs:
defer { ... if EXPR }

The second form is already allowed by the syntax proposed in this RFC, so the benefit of also allowing the first form seems to be little.


--
Arne Johannessen
<https://arne.johannessen.de/>
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 12:16:01 +0300
Sergey Aleynikov <sergey.aleynikov@gmail.com> wrote:

> - why invent a new verb for already existing semantic?
> - what do all current CPAN implementations lack, thus requiring this
> feature?
> - why has this to be a core feature, compared to yet another CPAN
> module?
> - why any existing module can't be bundled with Perl, achieving this
> functionality?

First I'm going to quote Nicholas Clark's lovely reply to this on a
different thread:

>> Trouble is, that Perl is a Turing complete language. Meaning that
>> *any* possible new feature that could be proposed can already be
>> implemented with existing functionality.

>> So arguing against a new feature because it is already possible is,
>> well, tautological.

Secondly I'm going to point out a few things:

* Aside from Syntax::Keyword::Defer, every other solution to this in
CPAN all wraps the deferred code in an anon sub. Core's defer, and
S-K-D do not. This has many effects but means, for example, that @_
behaves "unexpectedly"; and caller() gets confused.

* Additionally, all of the CPAN implementations besides S-K-D
implement the deferred code by either DESTROY on an object, or using
SAVEDESTRUCTOR_X to call_cv() an anon CV later on. In both of these
cases, the deferred code could throw an exception, which by
necessity is non-fatal to the surrounding code and is either
ignored, or simply printed with a warning. S-K-D, and my core
implementation, do not do this. They nest an inner RUNOPS loop,
meaning that exceptions behave "as expected" - a `die` inside
`defer` is thrown to the caller much like a `die` anywhere else.

* All of the CPAN implementations, including S-K-D, have unfortunate
interactions between next/last/redo (the "loopex" ops) and the defer
block. A true core implementation can forbid these.

while(1) {
defer { last }
}

will cause core to die with "Unable to 'last' out of a defer block"

whereas, any of the CPAN versions will either permit it but cause
further confusions, or outright segfault the interpreter.

In summary, *all* of the CPAN solutions are worse than core can
provide, because core can do things no CPAN solution (including
pluggable keywords) can do. Specifically in this case, the core
solution adds a whole new context stack type (CXt_DEFER) that the
loopex ops can inspect to complain about trying to next/last/redo
out of. Also `goto`.

Thirdly, to answer your final point about why not just bundle the syntax
module with core perl: If the module were able to *perfectly* perform
the same work as core perl, this could technically work. However, note
above that it can't (CXt_DEFER). But even if this were possible, I
don't think that is a very nice way forward. The entire point of
pluggable syntax modules is to allow CPAN-based experimentation of new
features (Function::Parameters became feature 'signature';
Syntax::Keyword::Try became feature 'try'). The successful end-goal of
that experimentation is promoting the feature to becoming true core
syntax. There is no point in core shipping a syntax plugin module,
because core can just do core things.

This third point will always be my answer whenever anyone says "well
why can't we just ship that syntax module with core".

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
Overall here I'm seeing a lot of feedback about the idea, but none
really about the way I have documented it. I thought that was the point
of this pre-RFC "just send an email" part of the process.

Since folks are engaging with the substance of the document, I will
judge that I've got the first draft "about right", and therefore add it
to the RFCs repo:

https://github.com/Perl/RFCs/pull/2

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
On 17/06/2021, Arne Johannessen wrote:

> Ben Bullock <benkasminbullock@gmail.com> wrote:
>>
>> I can see an advantage from being able to run defers conditionally.

> consider:

> defer if EXPR { ... }
> vs:
> defer { ... if EXPR }

> The second form is already allowed by the syntax proposed in this RFC,
> so the benefit of also allowing the first form seems to be little.

This first reads to me as implying that if EXPR is evaluated at the point
where the defer statement occurs, whereas the second if EXPR is clearly
evalauted at "defer time" .

Regards
Re: RFC 0004 - defer {} syntax [ In reply to ]
??????? Original Message ???????

On Thursday, June 17th, 2021 at 6:58 AM, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:

> Overall here I'm seeing a lot of feedback about the idea, but none
>
> really about the way I have documented it. I thought that was the point
>
> of this pre-RFC "just send an email" part of the process.
>
> Since folks are engaging with the substance of the document, I will

Sounds like a good idea to me. I suppose I was wrong; seems this was actually a pre-RFC (was not clear so that needs to be a tweak in the process - I have been trying to follow along).

Question for PSC: what's the indicator for submitting such a request? Could this have been done earlier in the "discussion"? What sort of discussions/comments are even appropriate for a pre-RFC? Seems to me as long as it's "well formed" and "high quality" the burden for creating a PR should be pretty low, since this is not the stage where the details I've been seeing in this thread are to be 'hashed out'.

>
> judge that I've got the first draft "about right", and therefore add it
>
> to the RFCs repo:
>
> https://github.com/Perl/RFCs/pull/2

So now I'll ask the RFC mediators; what's the next step here?

Brett

>
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
>
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
i like "ON_EXIT" better as a name for the block.

Also I published a working mechanism for this to CPAN, which hooks into the
local variable restoration through tie magic.

https://metacpan.org/pod/Scope::local_OnExit



On Thu, Jun 17, 2021 at 7:16 AM mah.kitteh via perl5-porters <
perl5-porters@perl.org> wrote:

>
> ??????? Original Message ???????
>
> On Thursday, June 17th, 2021 at 6:58 AM, Paul "LeoNerd" Evans <
> leonerd@leonerd.org.uk> wrote:
>
> > Overall here I'm seeing a lot of feedback about the idea, but none
> >
> > really about the way I have documented it. I thought that was the point
> >
> > of this pre-RFC "just send an email" part of the process.
> >
> > Since folks are engaging with the substance of the document, I will
>
> Sounds like a good idea to me. I suppose I was wrong; seems this was
> actually a pre-RFC (was not clear so that needs to be a tweak in the
> process - I have been trying to follow along).
>
> Question for PSC: what's the indicator for submitting such a request?
> Could this have been done earlier in the "discussion"? What sort of
> discussions/comments are even appropriate for a pre-RFC? Seems to me as
> long as it's "well formed" and "high quality" the burden for creating a PR
> should be pretty low, since this is not the stage where the details I've
> been seeing in this thread are to be 'hashed out'.
>
> >
> > judge that I've got the first draft "about right", and therefore add it
> >
> > to the RFCs repo:
> >
> > https://github.com/Perl/RFCs/pull/2
>
> So now I'll ask the RFC mediators; what's the next step here?
>
> Brett
>
> >
> >
> -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
> >
> > Paul "LeoNerd" Evans
> >
> > leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> >
> > http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
>


--
"Lay off that whiskey, and let that cocaine be!" -- Johnny Cash
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 at 21:32, David Nicol <davidnicol@gmail.com> wrote:

> i like "ON_EXIT" better as a name for the block.
>

Two issues I'd raise with this suggestion:

- the RFC explicits covers reasons not to use ALL-CAPS for naming.
Alternative suggestions should at least address those reasons with
counterpoints?
- it's triggered by scope end, not exit... so we'd be inconsistent with
both exit() (the keyword), and END (the phaser)
Re: RFC 0004 - defer {} syntax [ In reply to ]
it's a phaser, phasers are all-caps, whatever it's called. BLOCK_EXIT
maybe. My other often wished-for phaser is ONCE.

On Thu, Jun 17, 2021 at 9:01 AM Tom Molesworth <tom@deriv.com> wrote:

>
> - it's triggered by scope end, not exit... so we'd be inconsistent with
> both exit() (the keyword), and END (the phaser)
>
>
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, Jun 17, 2021 at 7:07 PM David Nicol <davidnicol@gmail.com> wrote:

> it's a phaser, phasers are all-caps, whatever it's called. BLOCK_EXIT
> maybe. My other often wished-for phaser is ONCE.
>

Is it? I would have expected a phaser to take (some) effect at compile
time. As would the RFC, it seems: " All the SHOUTY PHASER BLOCKS are
declarations, activated by their mere presence at compiletime, whereas
`defer {}` is a statement which only takes effect if dynamic execution
actually encounters it."

But I'm not sure what we should expect. What even is a phaser in Perl
5? Is this the occasion to land a definition?

(In Raku a phaser is documented to be a trait of a closure. It's not
clear to me if they could be applied after compilation, but they certainly
don't do so out of the box.)


Eirik
Re: RFC 0004 - defer {} syntax [ In reply to ]
and at compile time, a SCOPE_EXIT phaser would attach its coderef to the
exiting of the scope, encountered during run-time flow or not. Or
alternately, activated by having encountered it during run-time flow.
SCOPE_EXIT blocks if any (are active) would run before unlocalization of
package vars.


On Thu, Jun 17, 2021 at 12:35 PM Eirik Berg Hanssen <
Eirik-Berg.Hanssen@allverden.no> wrote:

> On Thu, Jun 17, 2021 at 7:07 PM David Nicol <davidnicol@gmail.com> wrote:
>
>> it's a phaser, phasers are all-caps, whatever it's called. BLOCK_EXIT
>> maybe. My other often wished-for phaser is ONCE.
>>
>
> Is it? I would have expected a phaser to take (some) effect at compile
> time. As would the RFC, it seems: " All the SHOUTY PHASER BLOCKS are
> declarations, activated by their mere presence at compiletime, whereas
> `defer {}` is a statement which only takes effect if dynamic execution
> actually encounters it."
>
> But I'm not sure what we should expect. What even is a phaser in Perl
> 5? Is this the occasion to land a definition?
>
> (In Raku a phaser is documented to be a trait of a closure. It's not
> clear to me if they could be applied after compilation, but they certainly
> don't do so out of the box.)
>
>
> Eirik
>


--
"Lay off that whiskey, and let that cocaine be!" -- Johnny Cash
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 14:18:02 -0500
David Nicol <davidnicol@gmail.com> wrote:

> and at compile time, a SCOPE_EXIT phaser would attach its coderef to
> the exiting of the scope, encountered during run-time flow or not. Or
> alternately, activated by having encountered it during run-time flow.
> SCOPE_EXIT blocks if any (are active) would run before unlocalization
> of package vars.

"code ref"

"encountered or not"

You seem to be describing a different thing that is not `defer {}`.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
really? you've said "defer <CODEREF>" repeatedly, and it sounded like the
message I was responding to brought up the question of if defered block
would happen always, or only when the defer keyword was encountered during
run-time flow.

You did not dispute my claim that local::on_scope_exit (or whatever I
called that CPAN module) does essentially the same thing.

"defer" is a mechanism for running something after the exiting of a code
block, a/k/a scope, but before the invoking code gets the returned value
back, is it not?

On Thu, Jun 17, 2021 at 2:25 PM Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

> On Thu, 17 Jun 2021 14:18:02 -0500
> David Nicol <davidnicol@gmail.com> wrote:
>
> > and at compile time, a SCOPE_EXIT phaser would attach its coderef to
> > the exiting of the scope, encountered during run-time flow or not. Or
> > alternately, activated by having encountered it during run-time flow.
> > SCOPE_EXIT blocks if any (are active) would run before unlocalization
> > of package vars.
>
> "code ref"
>
> "encountered or not"
>
> You seem to be describing a different thing that is not `defer {}`.
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
>


--
"Lay off that whiskey, and let that cocaine be!" -- Johnny Cash
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 14:35:27 -0500
David Nicol <davidnicol@gmail.com> wrote:

> really? you've said "defer <CODEREF>" repeatedly

I hope I haven't ever said that. I have mentioned `defer { BLOCK }`.

Blocks aren't coderefs.

E.g. consider the way that if(COND) { BLOCK } is not a coderef.

It's verymuch not a coderef - `caller()` can't see it, it doesn't get
its own `@_`, you can't `return` from it, etc...

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
From the keyboard of Paul "LeoNerd" Evans [17.06.21,20:49]:

> On Thu, 17 Jun 2021 14:35:27 -0500
> David Nicol <davidnicol@gmail.com> wrote:
>
>> really? you've said "defer <CODEREF>" repeatedly
>
> I hope I haven't ever said that. I have mentioned `defer { BLOCK }`.
>
> Blocks aren't coderefs.
>
> E.g. consider the way that if(COND) { BLOCK } is not a coderef.
>
> It's verymuch not a coderef - `caller()` can't see it, it doesn't get
> its own `@_`, you can't `return` from it, etc...

Well, a BLOCK passed to a sub is very likely a CODEREF - at least inside
the subroutine:

sub foo (&) {
say $_[0];
$_[0]->();
}
foo { say "calling foo()" }
__END__
CODE(0x55b2e63b2440)
calling foo()

So, a flow control block attached to if, for, while etc is not a CODEREF,
but one passed to a function is, e.g. sort { $b <=> $a }, also with map
although slightly different:

$f = sub { $_ x 2 };
say for map $f->(), qw(a b c);
__END__
aa
bb
cc

And since "defer" is a function, it takes a coderef.

0--gg-

--
_($_=" "x(1<<5)."?\n".q?/)Oo. G?\ /
/\_?/(q /
---------------------------- \__(m.====?.(_("always off the crowd"))."?
");sub _{s./.($e="'Itrs `mnsgdq Gdbj O`qkdq")=~y/"-y/#-z/;$e.e && print}
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 22:16:35 +0200 (CEST)
shmem <gm@qwurx.de> wrote:

> And since "defer" is a function, it takes a coderef.

The entire point here is that adding it as real syntax means defer
*isn't* a function. It's real native syntax, as real as

if(COND) { BLOCK }

while(COND) { BLOCK }

use feature 'try';
try { BLOCK } catch($VAR) { BLOCK }

As such, it can operate on a block that isn't implemented as a coderef.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 at 00:16, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

>
> be a SHOUTY PHASER BLOCK. All the SHOUTY PHASER BLOCKS are declarations,
> activated by their mere presence at compiletime, whereas `defer {}` is
> a statement which only takes effect if dynamic execution actually
> encounters it. The p5p mailing list and the CPAN module's RT queue both
> contain various other rejected naming ideas, such as UNWIND, UNSTACK,
> CLEANUP, to name three.
>
>
Unrelated note to this RFC, just observation:

From discussion about trim uppercase version of name seems better
alternative to me.
words like __PACKAGE__, AUTOLOAD, or taken from Ovid's Corinna, ADJUST,
these
tokens are not phaser and yet still there is a reason to have them
uppercase.

Maybe there should be some guideline when to use uppercase and when
lowercase.
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 22:32:46 +0200
Branislav Zahradník <happy.barney@gmail.com> wrote:

> words like __PACKAGE__,

__PACKAGE__, along with __FUNC__, __LINE__, __FILE__, etc.. are all
dunder-shouties, as they are constants. The parser literally replaces
them with a string (or number, in the case of __LINE__):

$ perl -MO=Deparse -ce 'my $file = __FILE__';
my $file = '-e';

> AUTOLOAD

AUTOLOAD is one of those weird things that probably should be specified
with `sub`; it's supposed to be

sub AUTOLOAD {
...
}

just that the parser lets you get away without the `sub`.

> or taken from Ovid's Corinna, ADJUST

Ah; but ADJUST _is_ a phaser. It's a block of code whose _mere
presence at compile time_ is sufficient:

LOOP: {
class AClass {
has $slot;

last LOOP;

ADJUST { $slot = 123; }
method greet { say "Slot is $slot" }
}
}

AClass->new->greet;
Slot is 123

Even though the line on which the ADJUST phaser was declared was never
dynamically reached (because we `last`ed out before it got there),
nevertheless the mere declaration of it was sufficient for it to be
active; much like the subsequent `method`.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, Jun 17, 2021 at 9:37 PM David Nicol <davidnicol@gmail.com> wrote:

> really? you've said "defer <CODEREF>" repeatedly, and it sounded like the
> message I was responding to brought up the question of if defered block
> would happen always, or only when the defer keyword was encountered during
> run-time flow.
>

If that was in reference to my message, I was not asking about defer (the
RFC was clear to me on that), but on phasers as such, a Perl 5 context. My
impression was we had different ideas about what that word means in a Perl
5 context.


Eirik
Re: RFC 0004 - defer {} syntax [ In reply to ]
I read the content of the Github issue again.

defer block is like END block that runs at the end of the scope, isn't it?

I feel this syntax is natural for Perl users now.

It seems that some pseudo-things are implemented on CPAN as well.
Re: RFC 0004 - defer {} syntax [ In reply to ]
Op 18-06-2021 om 03:16 schreef Yuki Kimoto:
> I read the content of the Github issue again.
>
> defer block is like END block that runs at the end of the scope, isn't it?
>
> I feel this syntax is natural for Perl users now.
>
> It seems that some pseudo-things are implemented on CPAN as well.
>

Yes and no. Yes, it's like that, because it runs on the end of a scope,
like END does at the end of the run. No, it's not like that as END
blocks always run when compiled, but defer is a statement that must be
executed for it to have it's effect.


HTH,

M4
Re: RFC 0004 - defer {} syntax [ In reply to ]
so what's wrong with just squidding it out in long form?

sub abcdefg {
my $defer;
...
my_predicate and $defer = sub {warn "MY_PREDICATE WUZ TROO!\n"};
...
$defer and &$defer;
$retval;
}

On Fri, Jun 18, 2021 at 3:55 AM Martijn Lievaart <m@rtij.nl> wrote:

> Op 18-06-2021 om 03:16 schreef Yuki Kimoto:
> > I read the content of the Github issue again.
> >
> > defer block is like END block that runs at the end of the scope, isn't
> it?
> >
> > I feel this syntax is natural for Perl users now.
> >
> > It seems that some pseudo-things are implemented on CPAN as well.
> >
>
> Yes and no. Yes, it's like that, because it runs on the end of a scope,
> like END does at the end of the run. No, it's not like that as END
> blocks always run when compiled, but defer is a statement that must be
> executed for it to have it's effect.
>
>
> HTH,
>
> M4
>
>
>

--
"Lay off that whiskey, and let that cocaine be!" -- Johnny Cash
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Fri, Jun 18, 2021 at 6:08 PM David Nicol <davidnicol@gmail.com> wrote:

>
>
> so what's wrong with just squidding it out in long form?
>
> sub abcdefg {
> my $defer;
> ...
> my_predicate and $defer = sub {warn "MY_PREDICATE WUZ TROO!\n"};
> ...
> $defer and &$defer;
> $retval;
> }
>

That won't run if scope is exited during the second ellipsis.

Whereas the defer "enqueues a block of code for later execution, when
control leaves its surrounding scope for whatever reason", so as long as
the defer statement is reached, the block will eventually run. (Well,
untimely program termination will prevent it, I guess.)


Eirik
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Fri, Jun 18, 2021 at 12:28 PM Eirik Berg Hanssen <
Eirik-Berg.Hanssen@allverden.no> wrote:

>
>
> On Fri, Jun 18, 2021 at 6:08 PM David Nicol <davidnicol@gmail.com> wrote:
>
>>
>>
>> so what's wrong with just squidding it out in long form?
>>
>> sub abcdefg {
>> my $defer;
>> ...
>> my_predicate and $defer = sub {warn "MY_PREDICATE WUZ TROO!\n"};
>> ...
>> $defer and &$defer;
>> $retval;
>> }
>>
>
> That won't run if scope is exited during the second ellipsis.
>
> Whereas the defer "enqueues a block of code for later execution, when
> control leaves its surrounding scope for whatever reason", so as long as
> the defer statement is reached, the block will eventually run. (Well,
> untimely program termination will prevent it, I guess.)
>

Yes, the primary usecase for scope guards is to run regardless of whether
the scope is normally exited. Abnormal scope exits include exceptions, loop
control flow, and returning from a subroutine, among more esoteric options.

-Dan
Re: RFC 0004 - defer {} syntax [ In reply to ]
very true. The rest of the single exit point pattern is having some
discipline about not exiting except via it.



>> Whereas the defer "enqueues a block of code for later execution, when
>> control leaves its surrounding scope for whatever reason", so as long as
>> the defer statement is reached, the block will eventually run. (Well,
>> untimely program termination will prevent it, I guess.)
>>
>
> Yes, the primary usecase for scope guards is to run regardless of whether
> the scope is normally exited. Abnormal scope exits include exceptions, loop
> control flow, and returning from a subroutine, among more esoteric options.
>
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, Jun 17, 2021 at 12:16:19PM +0000, mah.kitteh via perl5-porters wrote:
>
> ??????? Original Message ???????
>
> On Thursday, June 17th, 2021 at 6:58 AM, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:
>
> > Overall here I'm seeing a lot of feedback about the idea, but none
> >
> > really about the way I have documented it. I thought that was the point
> >
> > of this pre-RFC "just send an email" part of the process.
> >
> > Since folks are engaging with the substance of the document, I will
>
> Sounds like a good idea to me. I suppose I was wrong; seems this was actually a pre-RFC (was not clear so that needs to be a tweak in the process - I have been trying to follow along).

Right. So. You're on the money here. PSC thinks:

Start the process by mailing your "elevator pitch" to the list.

Specifically, send a short mail with 4 paragraphs that say:

1: Here is a problem
2: Here is the syntax that I'm proposing
3: Here are the benefits of this
4: Here are potential problems

(And if a "paragraph" is 1 sentence, great)


and that should be enough to make it obvious whether the idea is

0) worth drafting an RFC for
1) better on CPAN first
2) "nothing stops you putting it on CPAN, but it doesn't seem viable"


ie don't start out by taking the RFC template and attempting to fill it in.
Start with the summary above.


[.for now this is e-mail, and to this list. Once we've got the process
bootstrapped, it might turn into an "issue" template for the RFC repository.
But not yet]

> Question for PSC: what's the indicator for submitting such a request? Could this have been done earlier in the "discussion"? What sort of discussions/comments are even appropriate for a pre-RFC? Seems to me as long as it's "well formed" and "high quality" the burden for creating a PR should be pretty low, since this is not the stage where the details I've been seeing in this thread are to be 'hashed out'.
>
> >
> > judge that I've got the first draft "about right", and therefore add it
> >
> > to the RFCs repo:
> >
> > https://github.com/Perl/RFCs/pull/2
>
> So now I'll ask the RFC mediators; what's the next step here?

Um, well, technically the previous step was to mail the list with an idea,
which got skipped. (But I get why)

Also, according to my sketch of the "future" plans, that RFC draft should
have been an "Exploratory" RFC with a self-assigned ID. It shouldn't have
assumed that it would be 0004. (But I get why)


From this, I realise that "Draft" as a status name is a bad idea.
(Much like "Approved" isn't a name, to avoid confusion)

The status changes "Draft"/Provisional/Accepted/Implemented/Shipped/Stable
and what they stand for still makes sense - we just need a better name.


But, I was really pleased to read that RFC - Paul wrote an RFC that looks
like the style of document I was envisaging, with the right level of detail,
first time.

Until now I'd been the only person writing these things, so I didn't know if
we'd got the concept all wrong. It *might* still turn out that we have the
basic plan wrong, but right now it looks like it's still the right
direction, and it's more details that we need to iron out, not "redo from
start".


Anyway, what happens *next* is that I do the editorial checks on his "draft"
and put it in the repository (along with some other fixes that need doing)
However, I'm running out of time, and might not get a chance to do this
until tomorrow morning, or maybe even Monday morning. (Sorry. Life)

Also, PSC think that the status is now "Provisional" - ie we got here:

idea
|
v
mail p5p
|
better | rejected
on <-------?-------> with
CPAN | reasoning
v
Draft RFC
"we think this idea is worth exploring"
(Help us figure out how this will work)
|
v
Provisional RFC
"we think this idea is worth implementing"
(We have a firm idea of what we want to do)


because we have a pretty firm idea of what we want to do.

Nicholas Clark
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Wed, Jun 16, 2021, at 6:15 PM, Paul "LeoNerd" Evans wrote:
> # defer {} syntax

I'll be brief. First off, thanks, Paul, for doing a good job at dealing with this thread's ups and downs.

This seems like a reasonable proposal about which I have no strong feelings (yet). But I do have a question:

This is similar to, but not the same as, UNITCHECK. I would surely use this more often than I have used UNITCHECK, and I am definitely not here to say that defer is a phaser or redundant.

But when unitcheck was introduced, I remember thinking clearly and repeatedly that I'd have used it far often if it had been possible to inject the unitcheck upward one scope. Honestly, I'd have to do a fair bit more thinking about it to remember the use cases I had in mind. But you suggest (I think *very* hopefully) that we could eventually get away from timely destruction if we had better defer blocks. I think often we'd like defer blocks to be produced by reusable code, which does hint toward the idea of "defer this until my calling scope is complete."

Do you think this is an area in which we should consider nudging the capabilities of this proposal?

--
rjbs
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Sat, 19 Jun 2021 22:41:24 -0400
"Ricardo Signes" <perl.p5p@rjbs.manxome.org> wrote:

> But you suggest (I think *very* hopefully) that we could eventually get away from timely destruction if we had better defer blocks. I think often we'd like defer blocks to be produced by reusable code, which does hint toward the idea of "defer this until my calling scope is complete."

I understand that this was purely hypothetical, but nevertheless, I
want to make it clear that deterministic destruction is a core language
feature, more core than much of the syntax, and we can't remove it
without breaking pretty much everything. It isn't just a quirk of the
implementation.

RAII can't be implemented without deterministic destruction and
deterministic destruction can't be implemented without some kind of
object ownership mechanism. In Perl's case, it's reference counting.

Without RAII, there will be no automatic file closing, objects won't
have the ability clean-up after themselves. Languages based on pure
tracing garbage collection don't have those things. There's no cheat
code, it's impossible to implement. That's the reason why GC languages
have invented workarounds such as "try-with-resources" (Java), "using"
(C#) or, well, "defer".

To understand the problem with replacing deterministic destruction with
injectable defer blocks, let's imagine that open(), instead of returning
a refcounted filehandle with a destructor, injects a defer block to the
caller. The only reasonable thing that block could do is to close the
file at the end of the scope containing the open() call. That means it
would be impossible to pass the filehandle to the outer scope, e.g. by
"return". That's not good.
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Thu, 17 Jun 2021 at 20:24, Arne Johannessen <arne-9544@thaw.de> wrote:

> Ben Bullock <benkasminbullock@gmail.com> wrote:
> >
> > I can see an advantage from being able to run defers conditionally.
>
> consider:
>
> defer if EXPR { ... }
> vs:
> defer { ... if EXPR }
>
> The second form is already allowed by the syntax proposed in this RFC, so
> the benefit of also allowing the first form seems to be little.
>

I have not expressed any opinion about that.

use feature 'say';
use Syntax::Keyword::Defer;
while (1) {
if (1) {
defer {
say "A";
}
say "B";
}
say "C";
last;
}

prints

B
A
C

My suggestion is that it might be preferable to run the defer outside the
scope of the if block, so the above would print

B
C
A

I suggested this because I can't see how running a deferred block of code
at the end of an "if" block would be useful in practice, since, for example
there is no "next" or "last" which makes it possible to leave the if block
early, whereas being able to conditionally stack defers probably would be
useful.
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Sun, 20 Jun 2021 19:51:10 +0900
Ben Bullock <benkasminbullock@gmail.com> wrote:

> I suggested this because I can't see how running a deferred block of
> code at the end of an "if" block would be useful in practice,

The clean "mental model" of how it operates.

"defer {} runs its code when we hit the next nested }"

As opposed to

"defer {} runs its code when we hit the next nested } that wasn't an
if{} or elsif{} or unless{}, or maybe we'd consider adding do {} to
that list since you can't next out of them and what about match/case
blocks and ...."

`defer {}` runs at the same time that `my` variables go out of scope,
and `local` modifications get undone. This is by design to make it
simpler to learn and understand.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Sat, 19 Jun 2021 22:41:24 -0400
"Ricardo Signes" <perl.p5p@rjbs.manxome.org> wrote:

> But when unitcheck was introduced, I remember thinking clearly and
> repeatedly that I'd have used it far often if it had been possible to
> inject the unitcheck upward one scope. Honestly, I'd have to do a
> fair bit more thinking about it to remember the use cases I had in
> mind. But you suggest (I think *very* hopefully) that we could
> eventually get away from timely destruction if we had better defer
> blocks. I think often we'd like defer blocks to be produced by
> reusable code, which does hint toward the idea of "defer this until
> my calling scope is complete."
>
> Do you think this is an area in which we should consider nudging the
> capabilities of this proposal?

An exciting question. There's two parts to the answer:

On the question of how to spell the syntax, I could imagine if we're
going into the realm of core-provided namespaced functions and keywords
(e.g. previous discussions on `string::trim` and `ref::type`) I could
imagine an uplevel:: space for doing that kind of thing:

sub wibble {
uplevel::defer { say "This runs after Hello" }
}

{
wibble();
say "Hello";
}

As to implementation, I have no idea how to create this. `defer {}`'s
current implementation is just to use SAVEDESTRUCTOR_X() which pushes
to the same savestack as things like variable scope clearing and
`local` uses. That's strictly a stack, and you can't splice into it
lower down. There have variously been times I have wanted such
an ability, but equally other times (most notably while implementing
async/await) when I have been thankful for its absence.

I don't think I want to add this to the main body of the current
proposal, but I can at least add a note in "future direction" to
suggest that such an extension might become useful.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Sun, Jun 20, 2021, at 8:22 AM, Paul "LeoNerd" Evans wrote:
> I don't think I want to add this to the main body of the current
> proposal, but I can at least add a note in "future direction" to
> suggest that such an extension might become useful.

Thanks, sounds good. (The proposal clearly has value without this creeping feature, after all.)

--
rjbs
Re: RFC 0004 - defer {} syntax [ In reply to ]
2021-6-18 17:55 Martijn Lievaart <m@rtij.nl>:

> Op 18-06-2021 om 03:16 schreef Yuki Kimoto:
> > I read the content of the Github issue again.
> >
> > defer block is like END block that runs at the end of the scope, isn't
> it?
> >
> > I feel this syntax is natural for Perl users now.
> >
> > It seems that some pseudo-things are implemented on CPAN as well.
> >
>
> Yes and no. Yes, it's like that, because it runs on the end of a scope,
> like END does at the end of the run. No, it's not like that as END
> blocks always run when compiled, but defer is a statement that must be
> executed for it to have it's effect.
>
>
> HTH,
>
> M4
>
>
>
Martijn, thank you.
Re: RFC 0004 - defer {} syntax [ In reply to ]
How much does defer block affect current performance of Perl?
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Mon, 21 Jun 2021 at 07:55, Yuki Kimoto <kimoto.yuki@gmail.com> wrote:

> How much does defer block affect current performance of Perl?
>

The module is available on CPAN:

https://metacpan.org/pod/Syntax::Keyword::Defer

so this would be the best place to start if there are specific benchmarks
you would like to try.

Addition of the keyword should have no measurable impact on existing code.

Performance is generally faster than the equivalent
create-instance-and-hook-DESTROY functionality provided by modules such as
Scope::Guard.
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Sun, 20 Jun 2021 at 21:09, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

> On Sun, 20 Jun 2021 19:51:10 +0900
> Ben Bullock <benkasminbullock@gmail.com> wrote:
>
> > I suggested this because I can't see how running a deferred block of
> > code at the end of an "if" block would be useful in practice,
>
> The clean "mental model" of how it operates.
>
> "defer {} runs its code when we hit the next nested }"
>
> As opposed to
>
> "defer {} runs its code when we hit the next nested } that wasn't an
> if{} or elsif{} or unless{}, or maybe we'd consider adding do {} to
> that list since you can't next out of them and what about match/case
> blocks and ...."
>

Thank you for responding.

"next" and "last" within if statement blocks go to the surrounding loop
block rather than the if block. Similarly "return" in an "if" block returns
from the function rather than jumping out of the if block. I don't see
anyone complaining that that return or next or last do not provide a clean
mental model.

What I find confusing is that, as far as I know, there is not an obvious
way to exit the if{} without actually getting to the end of it, so

if (something) {
defer {
cleanup;
}
do something;
now do the deferred cleanup.
}

doesn't offer an advantage over

if (something) {
do something;
cleanup;
}

I went back and checked, and I've actually said this in all of the messages
I've sent on this thread, and so far nobody has pointed out the mistake in
my understanding. Is there some way of sneaking out of if{} without
arriving at the end of the code?

If "defer" is there to catch a "return" statement in the if block, then why
does "defer" need to be associated with the end of the if block? It could
just as well be associated with the end of the function for that case.
Similarly if it's meant to catch a "next" statement for an enclosing loop.
In both cases the scope which you are guarding is bigger than the if block,
it is either the function scope for the "return", or the loop scope for the
"next".

So I can't see what benefit there is in being able to defer to the end of
an if block scope. Did I miss something obvious?

It might be easier to understand for some people if set up this way, but
for me it was quite confusing why the defer would need to be run at the end
of if {}.
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Mon, 21 Jun 2021 at 09:36, Ben Bullock <benkasminbullock@gmail.com>
wrote:

> So I can't see what benefit there is in being able to defer to the end of
> an if block scope. Did I miss something obvious?
>
> It might be easier to understand for some people if set up this way, but
> for me it was quite confusing why the defer would need to be run at the end
> of if {}.
>

Because it happens when leaving scope - any scope, could be
if/while/try/catch/do...while 0/eval/bare block, they're all treated the
same, and this behaviour already applies for lexical scoping:

Anything introduced in the if() block - including `if(my
$variable_scoped_to_if_statement = ...)` - goes out of scope at the end of
the if() block.

Scope::Guard also triggers the guard sub when leaving the scope of the if()
block, rather than happening at some later point.

Unless you're proposing a change to that behaviour as well - something that
would cause a lot of breakage! - then having defer behave differently
introduces unnecessary consistency.

If you want something to execute in the parent scope, that can be done by
putting the defer above the if() statement. How would the following be
implemented with your suggested change in behaviour?

if(condition) {
open my $fh, '>', 'output' or die;
defer { $fh->close and send_email_from_file('output') }
... arbitrary code which may include next/return/die/goto ...
}
open my $fh, '>', 'output' or die;
...
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Sun, Jun 20, 2021, at 9:36 PM, Ben Bullock wrote:
> What I find confusing is that, as far as I know, there is not an obvious way to exit the if{} without actually getting to the end of it, so
>
> if (something) {
> defer {
> cleanup;
> }
> do something;
> now do the deferred cleanup.
> }
>
> doesn't offer an advantage over
>
> if (something) {
> do something;
> cleanup;
> }

Consider that "do something" might throw an exception.

--
rjbs
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Mon, Jun 21, 2021 at 3:37 AM Ben Bullock <benkasminbullock@gmail.com>
wrote:

> I went back and checked, and I've actually said this in all of the
> messages I've sent on this thread, and so far nobody has pointed out the
> mistake in my understanding. Is there some way of sneaking out of if{}
> without arriving at the end of the code?
>

Yes? Plenty of ways: An exception may sneak out (all the way to the
first dynamically enclosing eval or similar); a return may sneak out (all
the way out of the subroutine call); a next/last/redo may sneak out (all
the way out of the first dynamically enclosing loop with matching label, if
given); a goto LABEL may sneak out to some arbitrary LABEL.

And those evals, subroutine calls, and loops may nest in any order.
There's no one obvious outer context, like there's an obvious enclosing
block.


Eirik
Re: RFC 0004 - defer {} syntax [ In reply to ]
Still wading through all the messages that appeared over the weekend,
but this is the most subtly amusing thing I've read today (actually for
some days):

On Sun, Jun 20, 2021 at 01:22:16PM +0100, Paul "LeoNerd" Evans wrote:

> As to implementation, I have no idea how to create this. `defer {}`'s
> current implementation is just to use SAVEDESTRUCTOR_X() which pushes
> to the same savestack as things like variable scope clearing and
> `local` uses. That's strictly a stack, and you can't splice into it
> lower down. There have variously been times I have wanted such
> an ability, but equally other times (most notably while implementing
> async/await) when I have been thankful for its absence.

"With this hat on I want feature X"
"With that hat on I hate my other self"


Most of the Perl internals is like this. It's a love-hate relationship with
yourself, depending on whether you're authoring code for CPAN or
(re)implementing core functionality.

Nicholas Clark
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Mon, 21 Jun 2021 at 11:16, Eirik Berg Hanssen <
Eirik-Berg.Hanssen@allverden.no> wrote:

> On Mon, Jun 21, 2021 at 3:37 AM Ben Bullock <benkasminbullock@gmail.com>
> wrote:
>
>> I went back and checked, and I've actually said this in all of the
>> messages I've sent on this thread, and so far nobody has pointed out the
>> mistake in my understanding. Is there some way of sneaking out of if{}
>> without arriving at the end of the code?
>>
>
> Yes? Plenty of ways: An exception may sneak out (all the way to the
> first dynamically enclosing eval or similar); a return may sneak out (all
> the way out of the subroutine call); a next/last/redo may sneak out (all
> the way out of the first dynamically enclosing loop with matching label, if
> given); a goto LABEL may sneak out to some arbitrary LABEL.
>

No, that's not my point. If you have something like this

while (1) {
if (something) {
suddenlyexitif;
print "Did not execute.\n";
}
print "Did execute\n";
}

you have to come up with something at suddenlyexitif which leaves the if
block which does not execute "print "Did not execute.\n"" but does execute
"print "Did execute\n"", without adding labels.
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Mon, 21 Jun 2021 at 11:08, Tom Molesworth <tom@binary.com> wrote:

> If you want something to execute in the parent scope, that can be done by
> putting the defer above the if() statement. How would the following be
> implemented with your suggested change in behaviour?
>
> if(condition) {
> open my $fh, '>', 'output' or die;
> defer { $fh->close and send_email_from_file('output') }
> ... arbitrary code which may include next/return/die/goto ...
> }
> open my $fh, '>', 'output' or die;
>

I concede that you would be better off with the current implementation
rather than my suggestion if that is what you want to do with defer.
Re: RFC 0004 - defer {} syntax [ In reply to ]
2021-6-21 9:24 Tom Molesworth <tom@deriv.com> wrote:

> On Mon, 21 Jun 2021 at 07:55, Yuki Kimoto <kimoto.yuki@gmail.com> wrote:
>
>> How much does defer block affect current performance of Perl?
>>
>
> The module is available on CPAN:
>
> https://metacpan.org/pod/Syntax::Keyword::Defer
>
> so this would be the best place to start if there are specific benchmarks
> you would like to try.
>
> Addition of the keyword should have no measurable impact on existing code.
>
> Performance is generally faster than the equivalent
> create-instance-and-hook-DESTROY functionality provided by modules such as
> Scope::Guard.
>
>
I want to ask a question about real logical cost.

For example,

if (UNLIKELY(scope_has_deffer_blocks)) {

}

This may be 1 operation if the if block is not executed.
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Sun, 20 Jun 2021 at 14:23, Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

> sub wibble {
> uplevel::defer { say "This runs after Hello" }
> }
>
> {
> wibble();
> say "Hello";
> }
>
>
>
would in be better to use behaviour already used with redo/next/last ?

sub foo {
LABEL: {
if ($cond) { defer LABEL { ... } }
}
}
Re: RFC 0004 - defer {} syntax [ In reply to ]
how about some kind of way to inject coderefs into the call stack, perhaps
by using C<caller(n)> as an l-value?

sub wibble{ caller(0) = sub { say "This runs after Hello" } }

alternately

sub wibble{ push caller(0), sub { say "This runs after Hello" } }


> sub wibble {
>> uplevel::defer { say "This runs after Hello" }
>> }
>>
>> {
>> wibble();
>> say "Hello";
>> }
>>
>
--
"Lay off that whiskey, and let that cocaine be!" -- Johnny Cash
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Sun, Jun 20, 2021 at 8:23 AM Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

> On Sat, 19 Jun 2021 22:41:24 -0400
> "Ricardo Signes" <perl.p5p@rjbs.manxome.org> wrote:
>
> > But when unitcheck was introduced, I remember thinking clearly and
> > repeatedly that I'd have used it far often if it had been possible to
> > inject the unitcheck upward one scope. Honestly, I'd have to do a
> > fair bit more thinking about it to remember the use cases I had in
> > mind. But you suggest (I think *very* hopefully) that we could
> > eventually get away from timely destruction if we had better defer
> > blocks. I think often we'd like defer blocks to be produced by
> > reusable code, which does hint toward the idea of "defer this until
> > my calling scope is complete."
> >
> > Do you think this is an area in which we should consider nudging the
> > capabilities of this proposal?
>
> An exciting question. There's two parts to the answer:
>
> On the question of how to spell the syntax, I could imagine if we're
> going into the realm of core-provided namespaced functions and keywords
> (e.g. previous discussions on `string::trim` and `ref::type`) I could
> imagine an uplevel:: space for doing that kind of thing:
>
> sub wibble {
> uplevel::defer { say "This runs after Hello" }
> }
>
> {
> wibble();
> say "Hello";
> }
>
> As to implementation, I have no idea how to create this. `defer {}`'s
> current implementation is just to use SAVEDESTRUCTOR_X() which pushes
> to the same savestack as things like variable scope clearing and
> `local` uses. That's strictly a stack, and you can't splice into it
> lower down. There have variously been times I have wanted such
> an ability, but equally other times (most notably while implementing
> async/await) when I have been thankful for its absence.
>
> I don't think I want to add this to the main body of the current
> proposal, but I can at least add a note in "future direction" to
> suggest that such an extension might become useful.
>

I concur with this, and will add that such a concept would also be
incredibly useful for the `local` operator, so maybe both utilities could
be enhanced the same way.

-Dan
Re: RFC 0004 - defer {} syntax [ In reply to ]
>
>>
> I concur with this, and will add that such a concept would also be
> incredibly useful for the `local` operator, so maybe both utilities could
> be enhanced the same way.
>
> -Dan
>

I hate to play the killjoy, but a stack-hopping defer would represent both
a massive opportunity for exploitation and an explosion of spooky action at
a distance bugs.

--
"Lay off that whiskey, and let that cocaine be!" -- Johnny Cash
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Tue, Jun 22, 2021 at 9:34 PM David Nicol <davidnicol@gmail.com> wrote:

>
>
>>>
>> I concur with this, and will add that such a concept would also be
>> incredibly useful for the `local` operator, so maybe both utilities could
>> be enhanced the same way.
>>
>> -Dan
>>
>
> I hate to play the killjoy, but a stack-hopping defer would represent both
> a massive opportunity for exploitation and an explosion of spooky action at
> a distance bugs.
>

Something to reason out ("not reasonably viable" is a possible conclusion)
when such functionality is proposed, which as noted should definitely not
be done as part of this current effort.

-Dan
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Tue, 22 Jun 2021 at 20:16, David Nicol <davidnicol@gmail.com> wrote:

>
> how about some kind of way to inject coderefs into the call stack, perhaps
> by using C<caller(n)> as an l-value?
>
> sub wibble{ caller(0) = sub { say "This runs after Hello" } }
>
>
>
This syntax is limited to callers only.

From discussion here - it's clear to me that many of you think in the terms
of context oriented programming (without realizing it ...)

Looks like there are two kind of defer usage:
- code layout oriented
- data scope oriented

Although defer may be legit observing code layout.majority of usage may be
related to data frame cleanup (aka: defer (whenever $dbh is defined) { })

Speaking about loop exit operators: how it behaves in with dump ?

This still covers only "programmer-perfect" code, as implemented - doesn't
cover tests, reuse, where whole change one may need
is attach defer to addressable context from outside (see "define meta
operator")
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Wed, Jun 16, 2021 at 11:15:40PM +0100, Paul "LeoNerd" Evans wrote:
> Here's an RFC formalization of my currently-open github feature issue:

Thanks.

> https://github.com/Perl/perl5/issues/17949
>
> which was itself an issue tracking the previous mailing list
> conversation:
>
> https://www.nntp.perl.org/group/perl.perl5.porters/2020/06/msg257611.html
>
> If we accept this one into the RFC process, I'll likely close the
> github issue and point at the RFC instead.
>
> ---
>
> # defer {} syntax
>
> ## Pre?mble

The intent was that this section is machine parseable.

Technically this is the new PSC's call now, but

I know that you like diaereses, and I get that this spelling makes it clear
that it's two vowel sounds, but

* I don't mind what the section header is
* I didn't want it to have an ambiguous name
* I didn't want to unify on a name that isn't the common spelling

I can't find anywhere that suggests "pre?mble" as an alternative spelling.
eg

https://dictionary.cambridge.org/dictionary/english/preamble

(interestingly they have coordinate as "(also mainly UK co-ordinate)":
https://dictionary.cambridge.org/dictionary/english/coordinate

Even newyorker.com is using preamble, and, well, they send themselves up
about diaereses:

https://www.newyorker.com/culture/culture-desk/the-curse-of-the-diaeresis

So can we pick a different header name for this section that has one
unambiguous spelling, that is all ASCII? "Metadata" would do.

> Author: Paul Evans <PEVANS>
> Sponsor:
> ID: 0004

Technically according to the plan an RFC doesn't get an official ID until it
is accepted into the process. Right now things are slow enough that it
didn't matter.

> Status: Draft

I realised that everyone keeps talking about "Draft" RFCs when they are
drafting an RFC (well, d'oh) whereas the intent was that the is a clear
status that was "not yet accepted". So...

I swapped the names for the status of the first step, and for "not yet
accepted. "Exploratory" is now the first step in the repository, and "Draft"
is what comes before. So that "Status" line is retrospectively correct.

The status is now also "Provisional" because the outgoing PSC were pretty
confident "we think that this idea is worth implementing"


I've pushed a large update to the RFC repository that brings your RFC in.

Nicholas Clark
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Wed, Jun 16, 2021 at 11:15:40PM +0100, Paul "LeoNerd" Evans wrote:

> ## Motivation


> It would be nice to offer a native core syntax for this common
> behaviour.


: ((TODO - I admit I'm not very clear on how to split my wording between
: the Motivation section, and this one))

It seems much easier in theory that in practice. I *think* that it goes
roughly here (splitting your paragraph). Above this point is "problem I want
to solve" and below here is "my proposed solution".

Except that the next sentence flows nicely as part of the reasoning of *why*
so having the next line read "A simple explicit syntax for this" instead
would make a cleaner separation.


> A simple `defer { BLOCK }` syntax removes from the user any
> requirement to think about storing the guard object in a lexical
> variable, or worry about making sure it really does get released at the
> right time.


I don't think it really matters if you change the wording you had. But
take the TODO out - on balance I think you got it about as right as is
possible, but I still think that it's important to attempt to separate

Motivation - The existing problem
Rationale - My solution, and why it is good

even if those aren't the best names for the two.


> As well as being more convenient for users, it would help decouple Perl
> core's object behaviours, like `DESTROY`, from this task. If less code
> relies on DESTROY, it may one day be possible to change the way that
> part of the code works, without worrying so much about code that relies
> on it breaking.

IIRC after discussion you took this out, but I think that a variant of it
does belong here. Something like:

As well as being more convenient for experienced programmers, it would
make it easier to explain the code to newcomers. as it makes the desired
behaviour explicit, rather than behaviour emergent from deliberate
combination of side effects of core's object behaviours.
(ie what `DESTROY` is, and the timing of implicit object destruction)

Nicholas Clark
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Wed, Jun 16, 2021 at 11:15:40PM +0100, Paul "LeoNerd" Evans wrote:


Yes, I really like the idea.

> ## Prototype Implementation
>
> CPAN module `Syntax::Keyword::Defer` as already mentioned.
>
> In addition, I have a mostly-complete branch of bleadperl (somewhat
> behind since I haven't updated it for the 5.34 release yet) at
>
> https://github.com/leonerd/perl5/tree/defer

It's really useful to have a prototype implementation to play with.


There were a couple of things that I noticed

## The `return` error is runtime:

$ ./perl -Ilib -E 'use feature "defer"; sub foo { defer { return; } say "Hi"}; foo()'
defer is experimental at -e line 1.
Hi
Can't "return" out of a defer block at -e line 1.


Could this be detected at compile time (the peephole optimiser? Or even the
tokeniser?) and be a runtime error?

I realise that `last` and `next` are also errors if they attempt to exit
the defer block, but loops are allowed inside defer blocks, so I doubt
that they are as easy to reliably detect at compile time.


## Error handling differs between `eval` and not.

I think that this would be the first time in perl that the behaviour between
`eval` and not eval differs. In that without eval:

$ ./perl -Ilib -E 'use feature "defer"; sub foo { defer { die "One"; } defer { die "Two" } say "Hi"}; foo(); say "Bye"'
defer is experimental at -e line 1.
defer is experimental at -e line 1.
Hi
Two at -e line 1.
One at -e line 1.


We get both exceptions to STDERR and then the process exits:


$ ./perl -Ilib -E 'use feature "defer"; sub foo { defer { die "One"; } defer { die "Two" } say "Hi"}; eval { foo() }; say "Bye"'
defer is experimental at -e line 1.
defer is experimental at -e line 1.
Hi
Bye



whereas $@ can only capture one:

$ ./perl -Ilib -E 'use feature "defer"; sub foo { defer { die "One"; } defer { die "Two" } say "Hi"}; eval { foo() }; say "\$@ is $@"; say "Bye"'
defer is experimental at -e line 1.
defer is experimental at -e line 1.
Hi
$@ is One at -e line 1.

Bye
$


and if the block exit is caused by an exception:

$ ./perl -Ilib -E 'use feature "defer"; sub foo { defer { die "One"; } defer { die "Two" } die "Three"}; eval { foo() }; say "\$@ is $@"; say "Bye"'
defer is experimental at -e line 1.
defer is experimental at -e line 1.
$@ is One at -e line 1.

Bye
$ ./perl -Ilib -E 'use feature "defer"; sub foo { defer { die "One"; } defer { die "Two" } die "Three"}; foo(); say "\$@ is $@"; say "Bye"'
defer is experimental at -e line 1.
defer is experimental at -e line 1.
Three at -e line 1.
Two at -e line 1.
One at -e line 1.


it's consistent - the last error encountered is in $@. But errors get
eaten.

I'm struggling to suggest a better solution. I *think* that possibly $@
ought to be the *first* error encountered. As Klortho glibly put it:

#11916 Always ignore the second error message unless the meaning is obvious

but what's bugging me is that the second (or subsequent errors) might be
fallout from the problem that caused the first exception and error message,
but if that error message is lost (and not reported) it could be rather hard
to figure out that actual problem.


Do we also need to create a variable @@ which exposes the stack of exceptions
encountered? (With $@ set to one of them, last as you currently have it,
first as I'm suggesting, and unchanged for existing problematic situations,
such as exceptions firing in DESTROY)

Nicholas Clark
Re: RFC 0004 - defer {} syntax [ In reply to ]
the multiple exceptions behavior looks like a bug to be repaired, not a
feature to be accommodated.

IMO The first exception thrown from deferred code should prevent later
defer blocks in that scope from happening, just as if they were later in
the code text, instead of getting rearranged there through defer sugar.

sub foo { say "Hi"; die "two"; die "one" } # should this not be equivalent?

On Fri, Jul 2, 2021 at 4:45 AM Nicholas Clark <nick@ccl4.org> wrote:

>
> ## Error handling differs between `eval` and not.
>
> I think that this would be the first time in perl that the behaviour
> between
> `eval` and not eval differs. In that without eval:
>
> $ ./perl -Ilib -E 'use feature "defer"; sub foo { defer { die "One"; }
> defer { die "Two" } say "Hi"}; foo(); say "Bye"'
> defer is experimental at -e line 1.
> defer is experimental at -e line 1.
> Hi
> Two at -e line 1.
> One at -e line 1.
>
>
> We get both exceptions to STDERR and then the process exits:
>

--
"Lay off that whiskey, and let that cocaine be!" -- Johnny Cash
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Fri, Jul 2, 2021 at 12:58 PM David Nicol <davidnicol@gmail.com> wrote:

>
> the multiple exceptions behavior looks like a bug to be repaired, not a
> feature to be accommodated.
>
> IMO The first exception thrown from deferred code should prevent later
> defer blocks in that scope from happening, just as if they were later in
> the code text, instead of getting rearranged there through defer sugar.
>
> sub foo { say "Hi"; die "two"; die "one" } # should this not be
> equivalent?
>

No; the defer blocks were reached, so their scope cleanup should occur
whenever the scope is left, in this case because of an exception
propagating.

-Dan
Re: RFC 0004 - defer {} syntax [ In reply to ]
On Wed, Jun 16, 2021 at 11:15:40PM +0100, Paul "LeoNerd" Evans wrote:

Just a few random thoughts and observations:

First, I approve of the idea and the name.

* What value does defer return (in either scalar and list context)? E.g.

sub foo { defer { BLOCK } }
my $x = foo(); # what is $x?
my @x = foo(); # what is @x?

# (while I assume this is a syntax error):
my $x = defer { BLOCK };

* Does your implementation handle Deparse ok? (and B, Concise etc?)

* Does it peephole optimise the BLOCK? And does it test for it?

I usually test for this by putting an $a[0] in the code block and then
checking in t/perf/opcount.t that there is at least one aelemfast op
(which is what small constant array indices are optimised to).

* is it called on die(), exit(), exception, etc?

Is it clearly documented (and tested) what it does for every possible type
of scope exit? I see it croaks on last etc. Destructors are called on
exit() - is a defer block too?

* what happens if the sub gets deleted mid-flow:

sub foo {
defer { delete $::{foo} }
defer { .. do more stuff .. } # might this SEGV?
}


--
Decaffeinated coffee is like dehydrated water