Open leonerd opened 4 years ago
FTR, prior work on CPAN includes https://metacpan.org/pod/Scope::OnExit, https://metacpan.org/pod/Scope::Cleanup, and various scope guards like https://metacpan.org/pod/Scope::Guard
Further discussions about LEAVE have lead to an interesting question - What is the behaviour of exceptions thrown from them?
In a simple case such as
sub f {
return "123";
LEAVE { die "456" }
}
It is relatively easy to argue the case that this should die with "456 at ...", but complications come when the LEAVE block is being executed during stack unwind because another exception is being thrown.
sub g {
die "123";
LEAVE { die "456" }
}
It is fairly agreeable that invoking g() should die in some manner, but exactly how?
Taking a look around other things: Within perl we have many CPAN variations on this theme, almost all of which ultimately rely on DESTROY
methods being invoked on objects captured by block-scoped variables. Because exceptions during DESTROY
are turned into warnings, necessarily all of these must act the same way. Thus the 456 would be warned about but otherwise invisible to callers, who would receive the 123 exception.
My own Syntax::Keyword::Try offers a similar feature in the form of try/finally
blocks. These use the core SAVEDESTRUCTOR_X
feature which could be capable of propagating the exception but currently chooses not to due to some unanswered questions I have about how that would be represented.
Some other languages choose different ideas. For example, Java takes the latest exception and totally discards an earlier one (https://programming.guide/java/try-finally.html). Python appears to do similar, judging from some StackOverflow posts (though at present I am unable to locate specific words in the actual spec). Suggestions are that C# and JavaScript also do this.
In the case of the 123+456 case above, I can basically see three possible choices of behaviour:
a) Warn about 456, and propagate 123 to caller.
This is the current behaviour of SKT's try/finally, and also equivalent to how other CPAN options work)
b) Warn about 123, and propagate 456 to caller.
This is what most other langs do, except they don't even have a mechanism to warn about 123, so all information about 123 is lost entirely!
c) Combine 123 and 456 into a new "double fault" exception representation
This one would not be possible without a more standard form in which to represent exceptions in core perl. But see also https://github.com/Perl/perl5/issues/17951
(also posted to perl5-porters@ https://www.nntp.perl.org/group/perl.perl5.porters/2020/07/msg257949.html)
For those following along at home, this idea has been renamed back to FINALLY
and is being protoyped on CPAN: https://metacpan.org/pod/Syntax::Keyword::Finally
I think this really lacks flexibility without being able to change a 'return' value from a function, unlike a true 'finally' block.
I've never heard of such a feature in finally blocks. Do you have examples of prior art that include this feature? I don't find it particularly compelling, regardless; given Perl's notion of return context and dynamic optrees, this sounds quite complex, and I prefer the assurance that the block will indeed not alter any return values.
For example, java, see http://tpcg.io/PyA94RXu.
Notable from the Syntax::Keyword::Finally docs, as this differs a bit from the original proposal for LEAVE:
The operation can be considered a little similar to an END block, but with the following key differences:
- A FINALLY block runs at the time that execution leaves the block it is declared inside, whereas an END block runs at the end time of the entire program regardless of its location.
- A FINALLY block is invoked at the time its containing scope has finished, which means it might run again if the block is entered again later in the program. An END block will only ever run once.
- A FINALLY block will only take effect if execution reaches the line it is declared on; if the line is not reached then nothing happens. An END block will always be invoked once declared, regardless of the dynamic extent of execution at runtime.
Another update: the proposal is now being renamed to lowercase 'defer': https://metacpan.org/pod/Syntax::Keyword::Defer
I've now turned this issue into a draft RFC at https://github.com/leonerd/Perl-RFCs/blob/rfc0004/rfcs/rfc0004.md
The initial PR is now merged to blead. There are still some issues with it in odd situations, such as jumping out of a deferred block with goto &func
, or jumping in with a regular goto
. These will need fixing.
defer {...}
was first released in Perl v5.36 as an experimental feature.
There are currently no planned extensions to this experiment. It is now currently waiting out its bug-discovery period.
(copied from https://www.nntp.perl.org/group/perl.perl5.porters/2020/06/msg257611.html )
I'm trying to simplify the design of a
try/catch
syntax out of Syntax::Keyword::Try so it can be moved into core perl. One thing I think we can get rid of is thefinally
keyword, in favour of instead inventing a newLEAVE
phaser block, similar to the Raku ones.In summary: I'd like to add
LEAVE
with syntax which looks like another phaser such asEND
, but runs at the time you leave the block it is placed in:Will output
Furthermore, a LEAVE phaser inside something like a
foreach
loop would run every time you leave the block in order to run a new one:will output
Seems simple enough.
It is important to note that a LEAVE block is similar to an END block, in that the mere presence of that syntax is enough to queue it for being run, regardless of whether the line of code it's on was actually "reached". Thus, just as in
still prints, so too would
If we had a LEAVE phaser (totally independent of any thoughts of try/catch), it reduces the scope of that work considerably and makes it easier to work out how to implement.
Thoughts?