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

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

Thread Previous | Thread Next
From:
Paul "LeoNerd" Evans
Date:
July 16, 2020 21:25
Subject:
Re: try/catch and CLEANUP/FINALLY/&c.
Message ID:
20200716222459.3945e4be@shy.leonerd.org.uk
On Thu, 16 Jul 2020 08:48:20 -0400
Felipe Gasper <felipe@felipegasper.com> wrote:

> My understanding is that CLEANUP (“FINALLY”?) blocks essentially
> build into the language the functionality of modules like
> Scope::Guard, but without the Perl-level guard object.

That is exactly the plan, yes. This is one in several language design
tasks I'm looking at currently, to see what kinds of things the core
Perl language ought to provide, that currently everyone has to use one
of several CPAN implementations as workarounds for its lack.

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

  print "START[";
  CLEANUP { print "]STOP"; }

  ... # some other things to print in the middle.

The ability to insert multiple CLEANUP blocks inline in a single piece
of code helps to keep the logic clean and neat.

Consider

  try {
    my $mangler = Mangler->new;
    $mangler->setup;

    try {
      my $frobnicator = Frobnicator->new;
      $frobnicator->prepare_to_frob( $mangler );
      
      try { ... }
    }
    finally {
      $frobnicator->finish_frobbing;
    }
  }
  finally {
    $mangler->teardown;
  }

and compare to

  my $mangler = Mangler->new;
  $mangler->setup;
  CLEANUP { $mangler->teardown; }

  my $frobnicator = Frobnicator->new;
  $frobnicator->prepare_to_frob( $mangler );
  CLEANUP { $frobnicator->finish_frobbing; }

  ...

By using these cleanup blocks we have

  a) Avoided the "triangle of death" of ever-more indented code. If we
     insert a new stage with its own CLEANUP block, we won't need to
     further indent the rest by being wrapped in its own try/finally.

  b) Kept each piece of teardown logic lexically close to the setup
     logic that it complements. This avoids the reader having to scroll
     right to the end of the list to pair up the teardowns with the
     setups.

> Even for the more general case of “cleanup” logic, I’m not sure I
> find this:
> 
> 	CLEANUP { ... }
> 
> … more natural than the Scope::Guard pattern, where I can easily
> control both when the callback fires and whether to cancel it.

Yes; I accept that there is less control over these. In many ways
that's an advantage - because in *most* cases you don't need that extra
control. By not having it the code isn't cluttered by the extra noise
of having a control variable to store that guard object into, as would
be the case with Scope::Guard.

For those rare cases where you still need that control, of course you
can continue to use Scope::Guard or anything else. In code that
commonly uses just CLEANUP, the rareness of encountering a Scope::Guard
would call out to the reader "Something funky is going on here", and
they'd pay more attention to the subtle handling of your control
variable.

> How devoted are folks to the idea that a CLEANUP { .. } block will
> suffice as the means to implement try/catch/finally? For what it’s
> worth, I respectfully hope not overly so.

Currently, it's an idea I thought up a while ago and have just got
around to implementing; these discussions on perl5-porters@ are
entirely part of the overall language design process. That's the point
of discussing it here - to find out answers to that very question :)

-- 
Paul "LeoNerd" Evans

leonerd@leonerd.org.uk      |  https://metacpan.org/author/PEVANS
http://www.leonerd.org.uk/  |  https://www.tindie.com/stores/leonerd/

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