I've been thinking about the effect of the minimalist changes I
made to the RFC 88 reference implementation, and I don't see any
good reason not to support both the static and the dynamic forms
of end-of-block-scope actions.  Consider the following proposal.

 1. Support a try statement (a modified eval) which can have catch
    and finally clauses, using the semantics of RFC 88.  If you want
    to use catch or finally (instead of always or except) you have
    to use try.  These static catch and finally clauses come into
    consideration as at when the try is seen, independent of what
    happens in the block.

 2. Support always and except blocks.  These constructs may be used
    without requiring a try before the block.  They are dynamic
    operations which only come into play when they are encountered
    in the block, in run-time order.

 3. Implement always and except by converting them into equivalent
    dynamically-created catch and finally clauses in the block's
    actual or implied try statement.  Then the semantics of always
    and except, the details of their effect on exception propagation,
    and the details of conditional except blocks, are already
    defined (by RFC 88).

If you only want to use always and/or except that's fine, you don't
need the try.  If you don't want to use conditional catches and/or
excepts you don't have to.  We get the advantage of TMTOWTDI, while
keeping the details understandable and relatively simple to
implement by only using one set of semantic rules to cover both
cases.

If we take this approach then we know exactly what the following
code will do.

    { my $p = P->new();

      $p->foo and always { $p->bar };

      except Error::IO { $p->baz };
      }

We also know when the block propagates unwinding; in particular, it
doesn't if $p->foo raises an Error::IO exception, unless $p->baz
throws, but it does if $p->foo raises some other exception (or
if $p->foo doesn't raise an exception and $p->bar throws).

If we take this approach then when you just want to casually say

    my $f = open $file; always { close $f };

you can.  I like that.  In addition, when you want to carefully say

    my $result;

    foreach ... {

        try {
            my $p = Framework::P->new( ... );
            my $q = Framework::Q->new( ... );

            $result = Server::Foo( $p, $q, ... );
            }

        catch Exception::Framework::Bar {

            $result = Client::Fallback( $p, $q, ... );
            }

        finally { $p and $p->Done(); }
        finally { $q and $q->Done(); }

        last if $result;
        }

you can.  I like that too.  Easy easy, hard possible.

The more I think about having both these options available, the
better I like it.  Both allow me to be lazier (the latter in
the case where the exception handling logic is complicated
enough that doing it carefully is actually lazier).  I get the
impression some people think I want verbose code, or some sort
of impractial so-called "ivory tower" solution, but I'm really
just as lazy as you (probably lazier, but we don't want to debate
that here ;-)

Yours, &c, Tony Olekshy

Reply via email to