develooper Front page | perl.perl5.porters | Postings from July 2020

Re: try/catch and CLEANUP/FINALLY/&c.

Thread Previous | Thread Next
From:
Felipe Gasper
Date:
July 17, 2020 05:31
Subject:
Re: try/catch and CLEANUP/FINALLY/&c.
Message ID:
02A4974C-C270-4B54-B0A5-1126C8D4027C@felipegasper.com

> On Jul 16, 2020, at 5:24 PM, Paul LeoNerd Evans <leonerd@leonerd.org.uk> wrote:
> 
> On Thu, 16 Jul 2020 08:48:20 -0400
> Felipe Gasper <felipe@felipegasper.com> wrote:
> 
>> The well-known try/catch/finally pattern would then look thus:
>> 
>> {
>> 	CLEANUP { print 3 }
>> 
>> 	try {
>> 		print 1;
>> 		die 'oh no';
>> 	}
>> 	catch {
>> 		print 2;
>> 	}
>> }
>> 
>> To me--accustomed as I am to JS, Try::Tiny, &c.--this seems rather
>> clunky. The extra code block is ugly, and the execution path doesn’t
>> match the code layout. Compare to:
>> 
>> 	try {
>> 		print 1;
>> 		die 'oh no';
>> 	}
>> 	catch {
>> 		print 2;
>> 	}
>> 	finally {
>> 		print 3;
>> 	}
> 
> That's an artificially oddly-shaped example with just some prints. More
> typical in real use-cases are patterns involving obtaining some
> resource and initialising it in some way, with a scope guard/etc.. to
> shut it down again afterwards. The CLEANUP block wouldn't really just
> be "print 3" - in a real use-case it would be undoing the effect of
> something that had preceded it just above.

My examples were contrived, but they demonstrate the minimum necessary to match the execution flow of try/catch/finally using the proposed CLEANUP block.

If I do this:

-----
try {
	CLEANUP { .. }
}
catch { .. }
-----

… then my CLEANUP will fire before the catch block(s). But if I do this:

-----
CLEANUP { .. }

try { .. }
catch { .. }

print "hey";
-----

… then CLEANUP will run *after* the print statement. Thus, in order to have a “finally” block that fires right after the “catch”--or “try” if no error happened--I’d have to surround the try/catch with its own code block.

And maybe that’s OK. Maybe try/catch/finally is actually less useful in general than a CLEANUP that fires at the end of the block that contains the try/catch. I can’t say what’s most useful to a typical Perl developer--much less a “potential” Perl developer.

What I can say is that this pattern diverges from the pattern that most popular languages seem to implement. In TIOBE index order:

Java/Kotlin
Python
C#
Visual Basic
JavaScript
PHP
Ruby

… and some that don’t seem to be on TIOBE but retain substantial followings:

Julia
D
Delphi
Erlang
F#
Objective-C
Tcl

It’s also the same pattern found in popular CPAN implementations, including Try::Tiny, Try::Catch, and Syntax::Keyword::Try. So even for Perl developers, this is how people are used to working: with the “cleanup” block running immediately after the try/catch, not at the end of the enclosing block.

Consider also the pattern from promises, where then()/catch()/finally() mimics the corresponding pattern in synchronous code.

To be fair, Swift appears to handle “finally” the same way you propose: a generic scope-guard/cleanup block that’s independent of try/catch. So there’s at least precedent, however scant.

In short, the most widespread precedent by far is the “classic” try/catch/finally behavior. The advantages the CLEANUP {} pattern offers, IMO, don’t match the benefits Perl would realize from implementing what, going by the CPAN modules, even Perl developers, as well as potential Perl newcomers, will likely expect.

-FG
Thread Previous | Thread Next


nntp.perl.org: Perl Programming lists via nntp and http.
Comments to Ask Bjørn Hansen at ask@perl.org | Group listing | About