John M. Dlugosz has asked for the wisdom of the Perl Monks concerning the following question:

Consider code you want to run before leaving a block. Put it at the end of the block, right? Well, it becomes a mess if you have multiple returns or possibly even exceptions.

In C++, I could have a dummy object in the same scope having a destructor that did what I wanted.

That should be even simpler in Perl, where I can specify a block of code when constructing it!

So, has anyone done this yet?

Or, are there better ways to do this, such as using inner blocks/anonymous subs to cause the outer block to have a single return point anyway?

—John

Replies are listed 'Best First'.
(MeowChow) Re: 'finally' block in Perl?
by MeowChow (Vicar) on Jul 22, 2001 at 05:28 UTC
Re: 'finally' block in Perl?
by wog (Curate) on Jul 22, 2001 at 05:28 UTC
    While perl does not have a formal mechanism for finally blocks, you can indeed easily emulate them with an object that has a destructor. (update: MeowChow beat me by pointing out an easier/already-in-CPAN way to do this.)

    #>>> DummyDestructor.pm package DummyDestructor; use strict; use vars qw(@ISA @EXPORT); require Exporter; @ISA = qw(Exporter); @EXPORT = qw(make_finally); sub make_finally (\&) { return bless $_[0], __PACKAGE__; } sub DESTROY { $_[0]->(); } #>>> some perl script use DummyDestructor; ... { ... code ... my $final = make_finally { ... code ... } # beware! # no access to @_ in finally ... code ... }

    A more straight-forward alternative is eval and a bare block:

    sub f { my @return; ... more code ... # vars needed in "finally" must be # defined here! eval{{ ... and more ... # instead of return $retval you'd need: @return = $retval and last; ... yet more code ... }} ... do clean up stuff here ... die $@ if $@; return @return; }
      Why do you have two layers of { curly braces } delimiting the eval block?

        perlsyn describes how last, redo, or next won't work (or won't do what one might expect) in if, unless, do{}, sub{}, and eval{}. last says: "Note that a block by itself is semantically identical to a loop that executes once. Thus last can be used to effect an early exit out of such a block." - a solution also described in perlsyn: "Loop control statements don't work in an if or unless, since they aren't loops. You can double the braces to make them such, though. ... This is caused by the fact that a block by itself acts as a loop that executes once".

        Update: Note how in the following, the first part prints "A" and "B" (and a warning about exiting eval via last), but not "D", while the second part prints "X", "Y", and "Z".

        { print "A\n"; eval { print "B\n"; last; print "C\n"; }; print "D\n"; } { print "W\n"; eval {{ print "X\n"; last; print "Y\n"; }}; print "Z\n"; }
      Those are exactly the two methods I was thinking of. So what do people really do in this situation?
Re: 'finally' block in Perl?
by ariels (Curate) on Jul 22, 2001 at 11:06 UTC
    The statically scoped method is usually best. But another technique involving tied variables and local scoping is explored here: Abigail shows how to tie a variable to the cwd, then use
    tie $cwd => 'main'; # the example ties in package main { local $cwd = '/some/other/directory'; # execute in /some/other/directory } # resume in original working directory

    This technique will obviously work for other ties too; it is probably more appropriate when initialisation and finalisations are such paired opposites.

      Hmm, that means that the "poping" of the local going out of scope actually assigns the saved value back to the tied variable. I would have thought that the whole symbol is pushed somewhere and restored by changing its linkage.