Mailing List Archive

Pre-RFC: try/catch/finally and generic finally blocks
In perl 5.34, I implemented a small subset of the full
Syntax::Keyword::Try syntax, being just try/catch, with no finally and
no typed catches.

In perl 5.35.x (I forget which one) we now have `defer` blocks.

Using the same internals that makes them work it would be quite easy to
add the oft-requested try/catch/finally syntax:

try {
say "This happens first";
}
catch ($e) {
say "Oops, a failure happened";
}
finally {
say "This always happens, regardless of success or failure";
}

A try/catch/finally can be implemented basically the same way as if it
was written

{
defer { "finally" code goes here }

try {} catch($e) ... here as normal
}


I have to admit, that even though SKT allows one to write simply
try/finally with no catch, I have never really liked it. The trouble
with it is that as soon as the reader sees the word "try" they might
feel that this is nicely safe code, where any exceptions will be caught
and handled elsewhere. Only when they scroll down, possibly a long way,
and see the *lack of* catch, will they notice it isn't so safe.

try {
## 100 lines here
}
finally {
say "Oh and just do this before you go"
}

For that reason, I'm not sure I want to allow simply try/finally
without a catch.

However, many people like the "later code is written lower down" part
of try/finally. It reads somewhat backwards to see the defer {} block
before the main code:

defer { say "This happens second"; }
say "This happens first";

I wonder therefore about relaxing the syntax a bit to say that
actually, any bare block or a try/catch pair can be followed by a
`finally` block. Thus, we wouldn't write try/finally, but we could write

{
## 100 lines here
}
finally {
say "Oh and just do this before you go"
}

There was no `try` at the top, meaning the reader didn't get lulled
into a false sense of security by thinking there's exception-catching
going on here when there isn't. It's just a bare block, doing what bare
blocks do.

Speaking of do, perhaps you'd also permit

my $result = do { CODE HERE } finally { finish with this };

But that invites questions about value-semantics that I'm not too happy
about asking of what is essentially a control-flow syntax.


As a possible further extension, it could be worth thinking about
whether other kinds of blocks could also permit a trailing `finally` on
them. At the moment I don't have any strong feelings either way, though
offhand I can't immediately imagine what would be the semantics in other
situations:

foreach my $i ( 1 .. 10 ) {
say $i;
}
finally {
say "Done";
}


while( $count-- ) {
say "Nearly...";
}
finally {
say "Go!";
}


if( $x ) { ... }
elsif( $y ) { ... }
else { ... }
finally { say "When does this happen?" }


I don't think we should try too hard to give these meanings, as it's
rather unclear what they should do. Likely those should continue to be
syntax errors for now, and we just focus on the bare-block and
try/catch cases. At least for now.

If there doesn't turn out to be a clear consensus on how to proceed, I
probably won't bother adding anything at this stage. After all, these
postfixed "finally" blocks are really just a different spelling of
things that can already be achieved right now with `defer`; all it does
is moves the order of the lines in the source code so they read better
in top-to-bottom temporal order, and lets you get rid of a single set
of braces. It's a small syntax neatening but nothing truely
groundbreaking.


Thoughts anyone?

--
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
Re: Pre-RFC: try/catch/finally and generic finally blocks [ In reply to ]
> I don't think we should try too hard to give these meanings, as it's
rather unclear what they should do. Likely those should continue to be
syntax errors for now, and we just focus on the bare-block and
try/catch cases. At least for now.

I fully agree with this paragraph.
Bare blocks and try/catch pair fit best with finally.
Thanks.


On Tue, Oct 19, 2021 at 10:25 PM Paul "LeoNerd" Evans <
leonerd@leonerd.org.uk> wrote:

> In perl 5.34, I implemented a small subset of the full
> Syntax::Keyword::Try syntax, being just try/catch, with no finally and
> no typed catches.
>
> In perl 5.35.x (I forget which one) we now have `defer` blocks.
>
> Using the same internals that makes them work it would be quite easy to
> add the oft-requested try/catch/finally syntax:
>
> try {
> say "This happens first";
> }
> catch ($e) {
> say "Oops, a failure happened";
> }
> finally {
> say "This always happens, regardless of success or failure";
> }
>
> A try/catch/finally can be implemented basically the same way as if it
> was written
>
> {
> defer { "finally" code goes here }
>
> try {} catch($e) ... here as normal
> }
>
>
> I have to admit, that even though SKT allows one to write simply
> try/finally with no catch, I have never really liked it. The trouble
> with it is that as soon as the reader sees the word "try" they might
> feel that this is nicely safe code, where any exceptions will be caught
> and handled elsewhere. Only when they scroll down, possibly a long way,
> and see the *lack of* catch, will they notice it isn't so safe.
>
> try {
> ## 100 lines here
> }
> finally {
> say "Oh and just do this before you go"
> }
>
> For that reason, I'm not sure I want to allow simply try/finally
> without a catch.
>
> However, many people like the "later code is written lower down" part
> of try/finally. It reads somewhat backwards to see the defer {} block
> before the main code:
>
> defer { say "This happens second"; }
> say "This happens first";
>
> I wonder therefore about relaxing the syntax a bit to say that
> actually, any bare block or a try/catch pair can be followed by a
> `finally` block. Thus, we wouldn't write try/finally, but we could write
>
> {
> ## 100 lines here
> }
> finally {
> say "Oh and just do this before you go"
> }
>
> There was no `try` at the top, meaning the reader didn't get lulled
> into a false sense of security by thinking there's exception-catching
> going on here when there isn't. It's just a bare block, doing what bare
> blocks do.
>
> Speaking of do, perhaps you'd also permit
>
> my $result = do { CODE HERE } finally { finish with this };
>
> But that invites questions about value-semantics that I'm not too happy
> about asking of what is essentially a control-flow syntax.
>
>
> As a possible further extension, it could be worth thinking about
> whether other kinds of blocks could also permit a trailing `finally` on
> them. At the moment I don't have any strong feelings either way, though
> offhand I can't immediately imagine what would be the semantics in other
> situations:
>
> foreach my $i ( 1 .. 10 ) {
> say $i;
> }
> finally {
> say "Done";
> }
>
>
> while( $count-- ) {
> say "Nearly...";
> }
> finally {
> say "Go!";
> }
>
>
> if( $x ) { ... }
> elsif( $y ) { ... }
> else { ... }
> finally { say "When does this happen?" }
>
>
> I don't think we should try too hard to give these meanings, as it's
> rather unclear what they should do. Likely those should continue to be
> syntax errors for now, and we just focus on the bare-block and
> try/catch cases. At least for now.
>
> If there doesn't turn out to be a clear consensus on how to proceed, I
> probably won't bother adding anything at this stage. After all, these
> postfixed "finally" blocks are really just a different spelling of
> things that can already be achieved right now with `defer`; all it does
> is moves the order of the lines in the source code so they read better
> in top-to-bottom temporal order, and lets you get rid of a single set
> of braces. It's a small syntax neatening but nothing truely
> groundbreaking.
>
>
> Thoughts anyone?
>
> --
> Paul "LeoNerd" Evans
>
> leonerd@leonerd.org.uk | https://metacpan.org/author/PEVANS
> http://www.leonerd.org.uk/ | https://www.tindie.com/stores/leonerd/
>
Re: Pre-RFC: try/catch/finally and generic finally blocks [ In reply to ]
"Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
[...]
: try {
: ## 100 lines here
: }
: finally {
: say "Oh and just do this before you go"
: }
[...]
: {
: ## 100 lines here
: }
: finally {
: say "Oh and just do this before you go"
: }
:
:There was no `try` at the top, meaning the reader didn't get lulled
:into a false sense of security by thinking there's exception-catching
:going on here when there isn't. It's just a bare block, doing what bare
:blocks do.

On the other hand, there is also the possibility now that you end up
with:

{
# couple of lines here
}
# 100 lines of comments here
finally { ... }

I think it's unwise to try to mitigate every case of "100 lines here",
it seems like a short road to madness. But if you want to avoid it in
this case, I think you'd need yet another keyword. I'm not convinced
that the language is improved by tacking an optional continuation onto
bare blocks.

Either way, I do think we should be encouraging Perl developers to write
their code so as to minimize confusion, eg by factoring away "100 lines
here".

Hugo
Re: Pre-RFC: try/catch/finally and generic finally blocks [ In reply to ]
* hv@crypt.org <hv@crypt.org> [2021-10-19 21:49:47 +0100]:

> "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
> [...]
> : try {
> : ## 100 lines here
> : }
> : finally {
> : say "Oh and just do this before you go"
> : }
> [...]
> : {
> : ## 100 lines here
> : }
> : finally {
> : say "Oh and just do this before you go"
> : }

Replying to Hugo's comment only to honor it; but in the case of
the bare block this seems like a NOOP unless "finally" implies a
"wait" or waitpid for thing, but I don't know what things that
could be other than a child process spawned by either C<fork> or
an explict background shell command via C<system> or C<``> or
C<qx//>, etc.

LABEL:
{
# do stuff
# spawn a child proc
}
finally {
# do more stuff but what's
# the state of child process?
}

1. when does `finally` get executed, does it wait for child pid?
2. does LABEL: work as expected if one does, `next LABEL;`?

Cheers
Brett

> :
> :There was no `try` at the top, meaning the reader didn't get lulled
> :into a false sense of security by thinking there's exception-catching
> :going on here when there isn't. It's just a bare block, doing what bare
> :blocks do.
>
> On the other hand, there is also the possibility now that you end up
> with:
>
> {
> # couple of lines here
> }
> # 100 lines of comments here
> finally { ... }
>
> I think it's unwise to try to mitigate every case of "100 lines here",
> it seems like a short road to madness. But if you want to avoid it in
> this case, I think you'd need yet another keyword. I'm not convinced
> that the language is improved by tacking an optional continuation onto
> bare blocks.
>
> Either way, I do think we should be encouraging Perl developers to write
> their code so as to minimize confusion, eg by factoring away "100 lines
> here".
>
> Hugo

--
--
oodler@cpan.org
oodler577@sdf-eu.org
SDF-EU Public Access UNIX System - http://sdfeu.org
irc.perl.org #openmp #pdl #native
Re: Pre-RFC: try/catch/finally and generic finally blocks [ In reply to ]
On Tue, Oct 19, 2021 at 7:45 PM Oodler 577 via perl5-porters <
perl5-porters@perl.org> wrote:

> * hv@crypt.org <hv@crypt.org> [2021-10-19 21:49:47 +0100]:
>
> > "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
> > [...]
> > : try {
> > : ## 100 lines here
> > : }
> > : finally {
> > : say "Oh and just do this before you go"
> > : }
> > [...]
> > : {
> > : ## 100 lines here
> > : }
> > : finally {
> > : say "Oh and just do this before you go"
> > : }
>
> Replying to Hugo's comment only to honor it; but in the case of
> the bare block this seems like a NOOP unless "finally" implies a
> "wait" or waitpid for thing, but I don't know what things that
> could be other than a child process spawned by either C<fork> or
> an explict background shell command via C<system> or C<``> or
> C<qx//>, etc.
>
> LABEL:
> {
> # do stuff
> # spawn a child proc
> }
> finally {
> # do more stuff but what's
> # the state of child process?
> }
>
> 1. when does `finally` get executed, does it wait for child pid?
> 2. does LABEL: work as expected if one does, `next LABEL;`?
>

Forking has no relevance to the concept. The difference finally makes, as
with defer, is that it runs when the associated scope is exited, even if
it's done so via an exception, loop control, etc.

-Dan
Re: Pre-RFC: try/catch/finally and generic finally blocks [ In reply to ]
2021-10-20 4:07 Elvin Aslanov <rwp.primary@gmail.com> wrote:

> > I don't think we should try too hard to give these meanings, as it's
> rather unclear what they should do. Likely those should continue to be
> syntax errors for now, and we just focus on the bare-block and
> try/catch cases. At least for now.
>
> I fully agree with this paragraph.
> Bare blocks and try/catch pair fit best with finally.
> Thanks.
>

Me too.

try/catch pair with finally.

try {
say "This happens first";
}
catch ($e) {
say "Oops, a failure happened";
}
finally {
say "This always happens, regardless of success or failure";
}

And Bare block with finally

{
## 100 lines here
}
finally {
say "Oh and just do this before you go"
}
Re: Pre-RFC: try/catch/finally and generic finally blocks [ In reply to ]
* Dan Book <grinnz@gmail.com> [2021-10-19 20:48:15 -0400]:

> On Tue, Oct 19, 2021 at 7:45 PM Oodler 577 via perl5-porters <
> perl5-porters@perl.org> wrote:
>
> > * hv@crypt.org <hv@crypt.org> [2021-10-19 21:49:47 +0100]:
> >
> > > "Paul \"LeoNerd\" Evans" <leonerd@leonerd.org.uk> wrote:
> > > [...]
> > > : try {
> > > : ## 100 lines here
> > > : }
> > > : finally {
> > > : say "Oh and just do this before you go"
> > > : }
> > > [...]
> > > : {
> > > : ## 100 lines here
> > > : }
> > > : finally {
> > > : say "Oh and just do this before you go"
> > > : }
> >
> > Replying to Hugo's comment only to honor it; but in the case of
> > the bare block this seems like a NOOP unless "finally" implies a
> > "wait" or waitpid for thing, but I don't know what things that
> > could be other than a child process spawned by either C<fork> or
> > an explict background shell command via C<system> or C<``> or
> > C<qx//>, etc.
> >
> > LABEL:
> > {
> > # do stuff
> > # spawn a child proc
> > }
> > finally {
> > # do more stuff but what's
> > # the state of child process?
> > }
> >
> > 1. when does `finally` get executed, does it wait for child pid?
> > 2. does LABEL: work as expected if one does, `next LABEL;`?
> >
>
> Forking has no relevance to the concept. The difference finally makes, as
> with defer, is that it runs when the associated scope is exited, even if
> it's done so via an exception, loop control, etc.

I guess I don't understand the value of providing this for a bare block,
and the only value I can see in any other block structure is to extend
scope "one more time".

# currently
my $i;
for $i (1..10) {
# do stuff
}
if ($i < 10) {
print qq{Loop exited early!\n};
}

versus

# with 'finally'
for my $i (1..10) {
# do stuff
}
finally {
if ($i < 10) {
print qq{Loop exited early!\n};
}
}

Similarly, I see value in extending this to the block form of eval,

local $@;
my $ret = eval {
my $inner_var = q{asdf};
#... do stuf that might die
}
finally {
print qq{$inner_var};
}

#$@ and $ret available here, $inner_var is not
#...

It also seems to provide the corollary to do { .. } while (), which provides
an in scope "pre step" versus a basic while () { ... }.

So maybe to be consist, the "finally" on a "while" should be like,

while (...) {

}
do {

}

Or maybe, "finally" should just be "done" for all,

while (...) {

}
done {

}

or

try {

}
catch {

}
done {

}

or

for my $i (1..10) {

}
done {

}

etc

Thanks,
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: Pre-RFC: try/catch/finally and generic finally blocks [ In reply to ]
On Wed, 20 Oct 2021 at 12:57, Oodler 577 via perl5-porters <
perl5-porters@perl.org> wrote:

> I guess I don't understand the value of providing this for a bare block,
> and the only value I can see in any other block structure is to extend
> scope "one more time".
>
> # currently
> my $i;
> for $i (1..10) {
> # do stuff
> }
> if ($i < 10) {
> print qq{Loop exited early!\n};
> }
>

Consider `die`, `next OUTER` or `return` in the `# do stuff` section. Any
of those would give different results for the finally vs. code-after-loop
versions.