Mailing List Archive

Review of `defer` (was: FINALLY - ready for comments/review)
(resurrecting an old thread:
https://www.nntp.perl.org/group/perl.perl5.porters/2020/07/msg258096.html
)

I've finally (pardon the pun) got around to resuming my branch on this.

Since the original review, we've renamed the CPAN module to "defer"
because that seems to match a bunch of other languages. It's also been
implemented and tried out as a CPAN module:

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


The branch for core perl now lives at:

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


On Mon, 27 Jul 2020 11:53:52 +0100
Dave Mitchell <davem@iabyn.com> wrote:

> On Tue, Jul 21, 2020 at 11:47:55PM +0100, Paul "LeoNerd" Evans wrote:
> > It is now ready for general comment and review; I'll make a PR for
> > it soon.
>
> Some random comments:
>
> * using a PVOP - this is bad, and you should feel bad!!!! :-)
>
> I'd suggest using a LOGOP instead as a somewhat less torturing
> approach. If I understand correctly, the two OP pointers you want to
> hang off the OP_PUSHFINALLY are the root op and start op of the block.
>
> making logop->op_first point to the root op of the block is "normal"
> and should make everything work just fine: for example at the moment
> 'perl -DX' and 'perl -MO=Concise' don't appear to display the body of
> the block. Similarly, any B-type code will then automatically see the
> block, e.g. if scanning the op tree for some reason.
>
> The logop->op_other field is a generic pointer to some other op which
> is usually in some way an alternative to op_next under some
> circumstances; using this to store the block start is a slight
> stretch, but will generally work well;

Yup. That's now all done, based on following the same pattern that
worked for Syntax::Keyword::Defer, and List::Keywords.


> you just need to ensure that
> OP_PUSHFINALLY is treated the same as OP_AND etc in places like
> Perl_rpeep so that code within the block gets optimised.
>
> I'd also suggest adding a test to t/perf/opcount.t that with code like
> FINALLY { $a[0] } there is 1 aelemfast op present, proving that the
> code within the blocjk has been optimised.

Done.


> Similarly test that the block is processed by Perl_finalize_optree
> too, e.g. do eval '{ FINALLY { use strict; foo }' and check that you
> get 'Bareword "foo" not allowed'. Even if it works at the moment, it
> might not in future.

Done


> * why not just "use feature 'finally'" rather than 'finally_block'?

This is now renamed to `defer` anyway.


> * I think pp_pushfinally should be in pp_ctl.c rather than pp.c as
> it's related to flow control - its certainly where I would first look
> for it.

Moved.


> * your branch doesn't current compile under threads.

Huh, most most weird. Apparently you can't use croak() in pp_ctl.c when
under -Dusethreads, but you can without it. Highly strange.

In any case, now fixed.


> * I like the name LEAVE - why was it rejected?

(See above on renaming thoughts)


> * docs: the opening line:
>
> A block prefixed by the FINALLY modifier provides a section of
> code which runs at a later time in execution.
>
> I would change that to
>
> ... at a later time during scope exit.
>
> to more immediately emphasise what type of beast it is.

Done


> * docs: I would put explicit { }'s around your code examples to
> emphasise that things run on scope exit, as opposed to .. what? ..
> file exit??? etc.

Good idea.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
* Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> [2021-07-28 14:00:46 +0100]:

> (resurrecting an old thread:
> https://www.nntp.perl.org/group/perl.perl5.porters/2020/07/msg258096.html
> )
>
> I've finally (pardon the pun) got around to resuming my branch on this.
>
> Since the original review, we've renamed the CPAN module to "defer"
> because that seems to match a bunch of other languages. It's also been
> implemented and tried out as a CPAN module:
>
> https://metacpan.org/pod/Syntax::Keyword::Defer
>
>
> The branch for core perl now lives at:
>
> https://github.com/leonerd/perl5/tree/defer

Thanks for the CPAN module, this helped me understand what "defer" is;

This part made me get it more fully:

The operation can be considered a little similar to an END block

Since the topic of Try::Tiny and requiring a "catch" block has come up,
I feel like it's approproprite to request that there be some consistency
between a generic "defer" block and the optional "finally" block of Try::Tiny.

Questions:

a. does "defer" work if at the same "scope" as END? (and it's my understanding
that END is executed regardless of the predestined exit code (e.g., 0, 255 (die),
or something else).

#foo.pl
my $foo = q{bar};
defer { print qq{hi!\n} };
sub END {
print qq{bye!\n};
}

b. are there any effective differences between "defer" and Try::Tiny's "finally"?

c. if "b" is, "not fundamentally", would it behoove us to make "defer" and
"finally" consistent to the point that "defer" becomes "finally" and can be defined
either inside the block to which it apples or can be added to the end of
the block as "finally" appears to be in Try::Tiny ( I understand 'catch' and
'finally' are actually taken to be parameters of 'try', and prototype coersion
is utilize for the desired effect)?

Ultimately, this boils down to this - it seems "defer" and "finally" are similar
enough in nature (at least from the effective view of the developer), that it
would be highly beneficial to make them consistently applied from the developer
perspective. If we do not deeply consider this, then I am afraid we will be
introducing yet another inconsistency.

Thoughts?

I'd like to point on that Qore has "on_exit" for subroutines, which seems like
what "defer" would emulate if defined inside of a sub block.

https://docs.qore.org/qore-1.0.2/lang/html/statements.html#on_exit

I thought D lang had the same sort of thing, but I can't seem to find it at this
moment.

Cheers,
Brett

>
>
> On Mon, 27 Jul 2020 11:53:52 +0100
> Dave Mitchell <davem@iabyn.com> wrote:
>
> > On Tue, Jul 21, 2020 at 11:47:55PM +0100, Paul "LeoNerd" Evans wrote:
> > > It is now ready for general comment and review; I'll make a PR for
> > > it soon.
> >
> > Some random comments:
> >
> > * using a PVOP - this is bad, and you should feel bad!!!! :-)
> >
> > I'd suggest using a LOGOP instead as a somewhat less torturing
> > approach. If I understand correctly, the two OP pointers you want to
> > hang off the OP_PUSHFINALLY are the root op and start op of the block.
> >
> > making logop->op_first point to the root op of the block is "normal"
> > and should make everything work just fine: for example at the moment
> > 'perl -DX' and 'perl -MO=Concise' don't appear to display the body of
> > the block. Similarly, any B-type code will then automatically see the
> > block, e.g. if scanning the op tree for some reason.
> >
> > The logop->op_other field is a generic pointer to some other op which
> > is usually in some way an alternative to op_next under some
> > circumstances; using this to store the block start is a slight
> > stretch, but will generally work well;
>
> Yup. That's now all done, based on following the same pattern that
> worked for Syntax::Keyword::Defer, and List::Keywords.
>
>
> > you just need to ensure that
> > OP_PUSHFINALLY is treated the same as OP_AND etc in places like
> > Perl_rpeep so that code within the block gets optimised.
> >
> > I'd also suggest adding a test to t/perf/opcount.t that with code like
> > FINALLY { $a[0] } there is 1 aelemfast op present, proving that the
> > code within the blocjk has been optimised.
>
> Done.
>
>
> > Similarly test that the block is processed by Perl_finalize_optree
> > too, e.g. do eval '{ FINALLY { use strict; foo }' and check that you
> > get 'Bareword "foo" not allowed'. Even if it works at the moment, it
> > might not in future.
>
> Done
>
>
> > * why not just "use feature 'finally'" rather than 'finally_block'?
>
> This is now renamed to `defer` anyway.
>
>
> > * I think pp_pushfinally should be in pp_ctl.c rather than pp.c as
> > it's related to flow control - its certainly where I would first look
> > for it.
>
> Moved.
>
>
> > * your branch doesn't current compile under threads.
>
> Huh, most most weird. Apparently you can't use croak() in pp_ctl.c when
> under -Dusethreads, but you can without it. Highly strange.
>
> In any case, now fixed.
>
>
> > * I like the name LEAVE - why was it rejected?
>
> (See above on renaming thoughts)
>
>
> > * docs: the opening line:
> >
> > A block prefixed by the FINALLY modifier provides a section of
> > code which runs at a later time in execution.
> >
> > I would change that to
> >
> > ... at a later time during scope exit.
> >
> > to more immediately emphasise what type of beast it is.
>
> Done
>
>
> > * docs: I would put explicit { }'s around your code examples to
> > emphasise that things run on scope exit, as opposed to .. what? ..
> > file exit??? etc.
>
> Good idea.
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/

--
--
oodler@cpan.org
oodler577@sdf-eu.org
SDF-EU Public Access UNIX System - http://sdfeu.org
irc.perl.org #openmp #pdl #native
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
On Wed, 28 Jul 2021 17:48:47 +0000
Oodler 577 via perl5-porters <perl5-porters@perl.org> wrote:

> This part made me get it more fully:
>
> The operation can be considered a little similar to an END block
>
> Since the topic of Try::Tiny and requiring a "catch" block has come
> up, I feel like it's approproprite to request that there be some
> consistency between a generic "defer" block and the optional
> "finally" block of Try::Tiny.

Yes. `defer` is in many ways a prereq of adding `finally`, because once
OP_PUSHDEFER exists, then try/finally can simply use that in its
implementation.

> Questions:
>
> a. does "defer" work if at the same "scope" as END? (and it's my
> understanding that END is executed regardless of the predestined exit
> code (e.g., 0, 255 (die), or something else).
>
> #foo.pl
> my $foo = q{bar};
> defer { print qq{hi!\n} };
> sub END {
> print qq{bye!\n};
> }

I'm not sure I follow the question here. But here's the behaviour:

$ ./perl -Ilib -I. -E 'use feature "defer";
defer { say "Hi!" }
END { say "Bye!" }
'
defer is experimental at -e line 2.
Hi!
Bye!

> b. are there any effective differences between "defer" and
> Try::Tiny's "finally"?

defer happens at the exit of its own containing block.
finally of try/finally acts as if there was a containing block around
the try/finally statement.

{
defer { say "Later"; }
say "First";
}

equiv to

try { say "First"; }
finally { say "Later"; }

> c. if "b" is, "not fundamentally", would it behoove us to make
> "defer" and "finally" consistent to the point that "defer" becomes
> "finally" and can be defined either inside the block to which it
> apples or can be added to the end of the block as "finally" appears
> to be in Try::Tiny ( I understand 'catch' and 'finally' are actually
> taken to be parameters of 'try', and prototype coersion is utilize
> for the desired effect)?
>
> Ultimately, this boils down to this - it seems "defer" and "finally"
> are similar enough in nature (at least from the effective view of the
> developer), that it would be highly beneficial to make them
> consistently applied from the developer perspective. If we do not
> deeply consider this, then I am afraid we will be introducing yet
> another inconsistency.

Yes; they'll be implemented by the same underlying mechanism. See above.

> I'd like to point on that Qore has "on_exit" for subroutines, which
> seems like what "defer" would emulate if defined inside of a sub
> block.
>
> https://docs.qore.org/qore-1.0.2/lang/html/statements.html#on_exit
>
> I thought D lang had the same sort of thing, but I can't seem to find
> it at this moment.

Go's `defer` operates at the function level too. It's not a great fit
for Perl's very lexical nature.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
* Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> [2021-07-28 20:13:03 +0100]:

Thanks for the information, this is super helpful.

I'm still left to wonder that if, "defer" and "finally" are so similar
and in fact tied to a similar mechanism underneath; do they need to be
called different things - or is there an opportunity to simply go with
one or the other? And since Try::Tiny has already established "finally"
as way to schedule tied unconditional action, would it make sense to
proactively eliminate a potential source of confusion?

To be clear, I am not suggesting "defer" be renamed to "finally"; but
it seems like there potential to make them "look" the same.

What we will have:

# bare block
{
defer { do stuff .. at end of block };
...
}

# Try::Tiny (effectively what Try::Tiny is handing via prototyping coersion):
try {
...
}
catch {
...
}
finally {
...
}

Something more consistent:

# consistent with Try::Tiny
sub dostuff {

}
finally {

}

~or

sub dostuff {
finally { ... };
}

~or

# bare block (rather than 'defer')
{
finally { ... };
...
}

~or

{
...
}
finally {
...
}

Finally, I'd like to point out, ever just to get it on the record;
"defer" is not "like" the others here:

* sub END {}
* try {} catch {} finally {}
* { defer {} };

I'd also like point out since we're also discussing "concurrency";
that "defer" means something different in a concurrent or asynchronous
context (to me, someone who considered himself informated on the matter)
and in a "sequential context".

Based on the overarching point on my talk about sequential consistency
and the conflicts this causes on a high level depending on talking about
perl (sequential process) or Perl (syntax that naturally invites concurrent
semantics); I ask that some soul searching be done on this topic. Lest I be
accused of bikeshedding, on the contrary I believe we can avoid adding more
communication barriers (in the community) and also take advantage of this
opportunity to err on the side of "consistency" rather than add to the entropy
or chaos that clearly already weighs us down tremendously.

So what am I suggesting?

1. fine tune the semantics of "defer" considering both TT's "finally"
2. adjust the name of "defer" (if needed) considering the mental dissonace
forced upon us by this idea of perl (sequential) and Perl (begs concurrent
semantics).

Cheers,
Brett

> On Wed, 28 Jul 2021 17:48:47 +0000
> Oodler 577 via perl5-porters <perl5-porters@perl.org> wrote:
>
> > This part made me get it more fully:
> >
> > The operation can be considered a little similar to an END block
> >
> > Since the topic of Try::Tiny and requiring a "catch" block has come
> > up, I feel like it's approproprite to request that there be some
> > consistency between a generic "defer" block and the optional
> > "finally" block of Try::Tiny.
>
> Yes. `defer` is in many ways a prereq of adding `finally`, because once
> OP_PUSHDEFER exists, then try/finally can simply use that in its
> implementation.
>
> > Questions:
> >
> > a. does "defer" work if at the same "scope" as END? (and it's my
> > understanding that END is executed regardless of the predestined exit
> > code (e.g., 0, 255 (die), or something else).
> >
> > #foo.pl
> > my $foo = q{bar};
> > defer { print qq{hi!\n} };
> > sub END {
> > print qq{bye!\n};
> > }
>
> I'm not sure I follow the question here. But here's the behaviour:
>
> $ ./perl -Ilib -I. -E 'use feature "defer";
> defer { say "Hi!" }
> END { say "Bye!" }
> '
> defer is experimental at -e line 2.
> Hi!
> Bye!
>
> > b. are there any effective differences between "defer" and
> > Try::Tiny's "finally"?
>
> defer happens at the exit of its own containing block.
> finally of try/finally acts as if there was a containing block around
> the try/finally statement.
>
> {
> defer { say "Later"; }
> say "First";
> }
>
> equiv to
>
> try { say "First"; }
> finally { say "Later"; }
>
> > c. if "b" is, "not fundamentally", would it behoove us to make
> > "defer" and "finally" consistent to the point that "defer" becomes
> > "finally" and can be defined either inside the block to which it
> > apples or can be added to the end of the block as "finally" appears
> > to be in Try::Tiny ( I understand 'catch' and 'finally' are actually
> > taken to be parameters of 'try', and prototype coersion is utilize
> > for the desired effect)?
> >
> > Ultimately, this boils down to this - it seems "defer" and "finally"
> > are similar enough in nature (at least from the effective view of the
> > developer), that it would be highly beneficial to make them
> > consistently applied from the developer perspective. If we do not
> > deeply consider this, then I am afraid we will be introducing yet
> > another inconsistency.
>
> Yes; they'll be implemented by the same underlying mechanism. See above.
>
> > I'd like to point on that Qore has "on_exit" for subroutines, which
> > seems like what "defer" would emulate if defined inside of a sub
> > block.
> >
> > https://docs.qore.org/qore-1.0.2/lang/html/statements.html#on_exit
> >
> > I thought D lang had the same sort of thing, but I can't seem to find
> > it at this moment.
>
> Go's `defer` operates at the function level too. It's not a great fit
> for Perl's very lexical nature.
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/

--
--
oodler@cpan.org
oodler577@sdf-eu.org
SDF-EU Public Access UNIX System - http://sdfeu.org
irc.perl.org #openmp #pdl #native
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
On Wed, 28 Jul 2021 19:33:54 +0000
Oodler 577 via perl5-porters <perl5-porters@perl.org> wrote:

> I'm still left to wonder that if, "defer" and "finally" are so similar
> and in fact tied to a similar mechanism underneath; do they need to be
> called different things - or is there an opportunity to simply go with
> one or the other?

One could equally wonder why we'd need both `if` and `unless`, or both
`while` and `until`. Many language concepts can be expressed in terms
of others.

It's OK to have more than one similar way to express certain ideas.
People can pick whatever is the most convenient at the time. Some
situations will call for try/finally, other situations will call for
defer.

Already right now there's loads of code using things like Scope::Guard
that can be neatened up to using defer instead, without needing to
consider try/finally.

I think in a language as diverse as Perl, there is scope for both
[pardon the pun].

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
On Wed, Jul 28, 2021 at 3:34 PM Oodler 577 via perl5-porters <
perl5-porters@perl.org> wrote:

> * Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> [2021-07-28 20:13:03
> +0100]:
>
> Thanks for the information, this is super helpful.
>
> I'm still left to wonder that if...
>

Since I don't think they've been brought up in this thread, I recommend
reading through the existing proposals at
https://github.com/Perl/perl5/issues/17949 and
https://github.com/Perl/RFCs/blob/master/rfcs/rfc0004.md if you haven't.

-Dan
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
* Dan Book <grinnz@gmail.com> [2021-07-28 18:32:25 -0400]:

> On Wed, Jul 28, 2021 at 3:34 PM Oodler 577 via perl5-porters <
> perl5-porters@perl.org> wrote:
>
> > * Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> [2021-07-28 20:13:03
> > +0100]:
> >
> > Thanks for the information, this is super helpful.
> >
> > I'm still left to wonder that if...
> >
>
> Since I don't think they've been brought up in this thread, I recommend
> reading through the existing proposals at
> https://github.com/Perl/perl5/issues/17949 and
> https://github.com/Perl/RFCs/blob/master/rfcs/rfc0004.md if you haven't.

Great, Thank you!

Brett

>
> -Dan

--
--
oodler@cpan.org
oodler577@sdf-eu.org
SDF-EU Public Access UNIX System - http://sdfeu.org
irc.perl.org #openmp #pdl #native
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
2021-7-28 22:01 Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> worte:

>
> I've finally (pardon the pun) got around to resuming my branch on this.
>
>
I see the changes. It looks good to me (although I don't fully understand
the details).

My intuition is that some edge cases are likely to contain bugs(combination
with next, last, goto, BEGIN, INIT, CHECK, UNITCHECK, END), so I'd like a
lot of people to try the defer syntax.
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
On Wed, 28 Jul 2021 14:00:46 +0100
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:

> The branch for core perl now lives at:
>
> https://github.com/leonerd/perl5/tree/defer

There not being any show-stopping complaints thus far, I have now
opened an actual PR for this branch:

https://github.com/Perl/perl5/pull/19019

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
2021-8-4 1:59 Paul "LeoNerd" Evans <leonerd@leonerd.org.uk> wrote:

> On Wed, 28 Jul 2021 14:00:46 +0100
> "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
>
> > The branch for core perl now lives at:
> >
> > https://github.com/leonerd/perl5/tree/defer
>
> There not being any show-stopping complaints thus far, I have now
> opened an actual PR for this branch:
>
> https://github.com/Perl/perl5/pull/19019
>
>
Personally, I would like this to move forward.
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
On Tue, Aug 03, 2021 at 05:58:35PM +0100, Paul "LeoNerd" Evans wrote:
> On Wed, 28 Jul 2021 14:00:46 +0100
> "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
>
> > The branch for core perl now lives at:
> >
> > https://github.com/leonerd/perl5/tree/defer
>
> There not being any show-stopping complaints thus far, I have now
> opened an actual PR for this branch:
>
> https://github.com/Perl/perl5/pull/19019

I agree that there aren't any show stoppers.

I did raise a couple of questions in the previous thread that seem to have
got missed (so don't have answers):

https://www.nntp.perl.org/group/perl.perl5.porters/2021/07/msg260747.html

The one that bugs me most is:


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: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
On Fri, 6 Aug 2021 at 09:47, Nicholas Clark <nick@ccl4.org> wrote:

> On Tue, Aug 03, 2021 at 05:58:35PM +0100, Paul "LeoNerd" Evans wrote:
> > On Wed, 28 Jul 2021 14:00:46 +0100
> > "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
> >
> > > The branch for core perl now lives at:
> > >
> > > https://github.com/leonerd/perl5/tree/defer
> >
> > There not being any show-stopping complaints thus far, I have now
> > opened an actual PR for this branch:
> >
> > https://github.com/Perl/perl5/pull/19019
>
> I agree that there aren't any show stoppers.
>
> I did raise a couple of questions in the previous thread that seem to have
> got missed (so don't have answers):
>
> https://www.nntp.perl.org/group/perl.perl5.porters/2021/07/msg260747.html
>
> The one that bugs me most is:
>
>
> 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.
>

If one used a $SIG{__DIE__} one would have access to all the errors as they
occured, or I would expect I would.


>
> 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)
>

So an internal version of:

my @errors;
local $SIG{__DIE__}= sub { push @errors, @_; die @_};

Sounds useful, especially as it would leave __DIE__ handler chaining out of
the question.

cheers,
Yves

--
perl -Mre=debug -e "/just|another|perl|hacker/"
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
On Fri, 6 Aug 2021 07:46:47 +0000
Nicholas Clark <nick@ccl4.org> wrote:

> I did raise a couple of questions in the previous thread that seem to
> have got missed (so don't have answers):
>
> https://www.nntp.perl.org/group/perl.perl5.porters/2021/07/msg260747.html

Oops - I somehow manage to have missed those; let me reply here below
first:

> The one that bugs me most is:
>
>
> I think that this would be the first time in perl that the behaviour
> between `eval` and not eval differs. In that without eval:
...
> 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.

Yes; at the moment those are literally dropped on the floor. Perhaps we
could do another warning with prefix like we do with "(in cleanup)" now?

> 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)

A stack of exceptions.. hmmm..

I had a grander plan with these, which involved first getting some sort
of core exception type as a real object. That would solve lots of core
problems, plus make type `catch` more useful, and would allow an
exception object to represent "nested exception".

I'm not sure I like the idea of allowing an exception stack just to
workaround this (temporary?) situation wherein we don't have exception
objects yet.

Perhaps a nicer stepping stone would be to go with the prefixed-warning
approach; so e.g. an example would look like:

{
defer { die "Second failure"; }
die "First failure";
}

(during deferred cleanup) Second failure at example line 2.
First failure at example line 3.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
wondering how defer interacts with whatever, if anything, we've got in Perl
implementing the "promise" pattern.

TLDR: it doesn't.

Re-reviewing promises with their "then" methods (for instance,
https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md
) indicates that they are entirely orthogonal, except that it might be
possible to implement defer with promises somehow, if promises was your
main flow control mechanism.

something like rewriting blocks with defer in them, to

ALL_THE_NON_DEFERRED_CODE->then( THE_DEFERRED_CODE )

But as defer is concerned with ordering of control within a block, it
wouldn't have side effects that would in any way hop from promise to
promise, given a promise library such as Yanick Champoux
<https://metacpan.org/author/YANICK>'s Promises, which uses something
called "deferred" to postpone an action to the next time around the event
loop, instead of immediately. Again, the defer we are discussing here is
not concerned with asynchronous event loops. One might use defer to
reorganize code within a callback, but that's it.

Thank you for your patience

dln

On Wed, Jul 28, 2021 at 2:13 PM Paul "LeoNerd" Evans <leonerd@leonerd.org.uk>
wrote:

>
> > b. are there any effective differences between "defer" and
> > Try::Tiny's "finally"?
>
> defer happens at the exit of its own containing block.
> finally of try/finally acts as if there was a containing block around
> the try/finally statement.
>
> {
> defer { say "Later"; }
> say "First";
> }
>
> equiv to
>
> try { say "First"; }
> finally { say "Later"; }
>
>
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
On Fri, 6 Aug 2021 13:42:22 -0500
David Nicol <davidnicol@gmail.com> wrote:

> wondering how defer interacts with whatever, if anything, we've got
> in Perl implementing the "promise" pattern.
>
> TLDR: it doesn't.

Since defer blocks live on the savestack, they'll interact just fine
with the async/await syntax provided by Future::AsyncAwait:

use feature qw( say signatures defer );
use Future;
use Future::AsyncAwait;

async sub x ($f) {
say "1";
defer { say "4"; }
await $f;
say "3";
}

my $f1 = Future->new;

say "0";
my $fret = x($f1);

say "2";
$f1->done;

say "5";

Results in all 6 messages being printed in numerical order -
specifically note that 4 comes after both 2 and 3.

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Review of `defer` (was: FINALLY - ready for comments/review) [ In reply to ]
This is a proposal.

Is it possible to follow a link to RFCs from the perl5 repository?

From
https://github.com/Perl/perl5

To
https://github.com/Perl/RFCs

The ways I think.

1. README or
2. Wiki