develooper Front page | perl.perl6.language | Postings from February 2001

Re: End-of-scope actions: Toward a hybrid approach.

Thread Previous | Thread Next
From:
Glenn Linderman
Date:
February 17, 2001 11:25
Subject:
Re: End-of-scope actions: Toward a hybrid approach.
Message ID:
3A8ECD2A.535C76C@Linderman.com
Tony Olekshy wrote:

> Glenn Linderman wrote:
> >
> > Tony Olekshy wrote:
> > >

> > By rule 2 above, it would seem that if $p->foo raises an Error:IO
> > exception, that the except block hasn't yet been seen, and
> > therefore the block should propagate unwinding.
>
> Ah, yup.  ++$bugs{$self}

OK, good.  I was scared that if you didn't consider that a bug, that I
really, really, didn't know what you were talking about.  I think we're
getting pretty close, now, to understanding and appreciating each others
points of view.  I perceive that exception handling doesn't always have
to be hard, and want easy things easy.  You (perhaps) want every last
detail fully understood, preferably with a Perl 5 implementation, so you
tackle all the hard problems up front.  These two are not fully
exclusive of each other, although I think you realize that Perl 5 cannot
handle everything we want for Perl 6, and by considering all the hard
cases up front, and by trying to make a Perl 5 implementation, you came
up with an easy things hard solution in RFC 88.

> > [In RFC 119] The except clause [...] was intended to catch the
> > exceptions of only the statement to which it was attached.  I like
> > the conditional syntax you have added to the except clause, to
> > allow catching only specific exceptions.  However, in making it a
> > standalone statement, the semantics change significantly.  As a
> > clause, the except clause could be attached to the single
> > statement (or block) from which exceptions it was supposed to
> > handle might be propagated.  As a statement, however, it seems to
> > have acquired a block level scope [...]
>
> I think I know what happened. Back in the 'Assign to magic
> name-of-function variable instead of "return"' thread, the
> discussion of the dangling always block came up again.  John
> Porter pointed out that the difficulty of determining the scope
> of the operation is decreased if the always block is placed
> inside the block to which it applies, not after it (more below).
> I used that notion in proposing the hybrid, and inadvertenly broke
> some of the functionality of RFC 119's dangling except block.

I think that is what happened.  I noticed John's comments too.  In fact,
you'll note that RFC 119 does, in fact, place catch statements inside
the scope of the block to which they apply, which addresses John's
comments.

So in RFC 119, we have in-scope catch statements, dangling except
clauses, and dangling always clauses.  Until I read your post to which I
am
responding, I thought the always clause can be easily converted to an
always statement immediately preceding the statement to which the clause
was
attached, if such an always statement is defined. I no longer believe
that, and will discuss that below.  Long live the always clause.

However, the except clause is more complicated, and more powerful. 
Neither of your alternatives below fully capture the power of the
except clause, although the except clause can be used to achive either
of the alternatives below.  I'm not sure how to describe the except
clause
in terms of RFC 88 syntax-- that doesn't mean that it can't be done, and
I'll attempt to describe the except clause semantics in terms of RFC 88
semantics +
verbage, and see if you can achieve a syntactical equivalence using RFC
88
syntax.

First, some response to your alternatives so you can see where they
differ from the RFC 119 except clause.

> > The RFC 119 except clause, which was joined to the prior
> > statement, affected only that prior statement.  In other words,
> > the except clause and always clause had different relationships
> > to the statement to which they were attached.
>
> I'm concerned that I may not understand what you mean by that.
> I see two alternatives for the dynamic behaviour of the except
> block, which I'd like to try to explain by considering their "try"
> dualities.  Then perhaps you can let me know which you mean, or if
> I'm still missing something.

I think you are still missing something.

> Alternative 1:
>
>     Start with the following code, as written by a developer:
>
>         { a;
>           b except { c };
>           d;
>           }
>
>     Assume, for the sake of discussion, that Perl 6 blocks have
>     try-like semantics, as if the programmer had written:
>
>         try {
>           a;
>           b except { c };
>           d;
>           }
>
>     Now, after statement a (if it did not throw) and *before* b
>     (not after it, like an except statement would), Perl dynamically
>     converts the except block into an equivalent catch clause and
>     unshifts it onto the try statement's pending clause's list.
>     From that run-time point on, it's as if we were executing:
>
>         try {
>           b;
>           d;
>           } catch { c };
>
>     Perl then continues executing statement b.

This alternative would be written with except as:

     {  a;
        { b;
          d;
        } except { c };
     }

>     CONSIDERATIONS:
>
>         c is executed only if b or d throw.
>
>         Unwinding is propagated if b or d throw,
>         unless c does not throw.
>
> Alternative 2:
>
>     The following code:
>
>         { a;
>           b except { c };
>           d;
>           }
>
>     Is the same as:
>
>         { a;
>           try { b } catch { c };
>           d;
>           }

This alternative would be written with except as:

          { a;
            { b except { c }}
            d;
          }

>     CONSIDERATIONS:
>
>         c is excecuted only if b throws.
>
>         Unwinding is propagated if b throws, unless c
>         does not throw.  Unwinding is also propagated
>         if d throws.
>
> So, given those considerations, is one of those alternatives
> the dual of your concept for the dynamic behaviour of the except
> block?

I'll attempt now to describe except in terms of try semantics + verbage,
since I don't know how to do it with try syntax.

Alternative 1's description is closest.  However, after the successful
execution of b (b doesn't throw), the "implicit enclosing try" block
should have the "equivalent catch clause" that was unshifted onto "try
statement's pending clauses list" shifted back off of that list.

> Now on to the matter of the scope of the dangling block construct.
> I think I understand the "a except { b }" case: the (deferred)
> execution of b (iff unwinding) comes into effect just before
> executing a.  In the case of:
>
>     a and b except { c }

It should be bound to as much preceding stuff as it can be, because it
can be constrained with additional {} when desired.  To answer your
questions:

> Does the deferred execution of c iff unwinding come into effect just
> before a, or just before b (and only if a is true)?

Just before a, and removed just after b is succesful.
To achieve b only, write instead:

      a and { b except { c }}

Of course, the introduction of a new block changes the semantics of a
successful except somewhat, because if c succeeds, additional statements
in the block containing this fragment will execute.  In other words, we
need a bigger example.  Start with:

   {  a and b except { c };
      d;
   }

If a or b fails, c is executed, but not d, whether or not c succeeds or
fails.  If c succeeds, no unwinding takes place after the block shown. 
If c fails, additional unwinding takes place.  With

    {  a and { b except { c }};
       d;
    }

if a fails, b, c, and d will not be executed.  If b fails, c will be
executed, with d being then executed if c succeeds.  If b is successful,
c is not executed, even if d fails.

>  And in the case of:
>
>     if ( a ) { b } else { c } except { d }
>
> Does the deferred execution of d iff unwinding come into effect just
> before the "if", or just before the "else" (and only if a is false)?

Just before if, and removed just after b or c is successful (depending
on a).

To achieve else only, write instead:

     if ( a ) { b } else { c except { d }}

> Does this work:
>
>     if ( a ) { b } except { c } else { d } except { e }

No.  This is bizarre.  The syntax of if requires else after the block
containing b.  To achieve an effect you may be trying to express, write
instead:

     if ( a ) { b except { c }} else { d except { e }}

However, the same comments as above regarding the additionally
introduced block also apply here.

> For reference, here are all the scoping alternatives just discussed,
> using try's notation:
>
>     try { a } catch { b }
>
>     try { a and b } catch { c }
>
>     a and try { b } catch { c }
>
>     try { if ( a ) { b } else { c } } catch { d }
>
>     if ( a ) { b } else { try { c } catch { d } }
>
>     if ( a ) { try { b } catch { c } }
>     else     { try { d } catch { e } }

Indeed, you also introduce additional blocks to achieve similar syntax. 
So I think that you are only trying to argue the point that because a
dangling block introduces some precedence type binding in particular
cases,
and you were uncertain where I wanted to place it (and those cases were
not
explicitly addressed in RFC 119), that you couldn't be sure how to treat
them.  That's fair; I answered; I think the result is consistent with
RFC 119, and RFC 119 could be extended with these cases to be more
completely explicit.  If the RFC period were still open, I would revise
RFC 119 to contain these cases.

> Finally, I'd like to revisit the matter of always.  First consider
> the case where always is a dangling block like except.  In such a
> case, the following could be written:
>
>     $x = f() always { g($x) };
>
> which would call g if f throws.  With the standalone statement
> behaviour, wouldn't I have to write:
>
>     always { g($x) };  $x = f();

In general, yes.  In particular cases, it may not matter.  One such case
might be:

     open F, $f  always close F.

However, this raises an interesting point that I hadn't considered in
quickly agreeing to an always statement.  By the way, I still have
nothing against an always-like statement, as an addition to an always
clause.

Some IO RFC suggests converting the above to

    my $fh = open $f  always close $fh;

This cannoct be easily rewritten as a preceeding always statement,
because of the scope of $fh.  So the clause is more powerful.  To flesh
out
the examples, the first below would not work, therefore the more
cumbersome
second example would be required (and annoying).

    always close $fh;
    my $fh = open $f;


    my $fh;
    always close $fh;
    $fh = open $f;


> If y'all can agree on a set of interpretations similar to a sub-set
> of those described above, then I can extend the syntax defined at
> http://www.avrasoft.com/perl6/rfc88.htm#Syntax to include except and
> always, and completely define the details of their behaviour by
> using their try dualities.

Tony, you are the try statement perfectionist.  You'll have to come up
with the try statement syntax and semantics that are equivalent to these
examples; I cannot.  I attempted to describe the behavior, but I can't
presently see that all the examples possible with except clauses are
possible with the try statement, as currently defined by RFC 88 or your
recent extensions.  Perhaps some additional extensions will do the
trick, but you'll have to do that.

> I can't extend the reference model past
> what I've done, because of the implied try scopes, but we could do
> enough with a source code filter to update the RFC 88 regression
> tests to cover except and always via their dualities.
>
> That could benefit those of you who prefer using the except and always
> constructs, because then they could be understood in terms of the
> RFC 88 Perl 5 reference implementation.  If we want to get except and
> always into Perl 6, this approach could help provide the details needed
> to complete http://tmtowtdi.perl.org/rfc/119.html#IMPLEMENTATION.

Well, I have a slightly different view point here.  I think the RFC 119
semantics are reasonably completely specified.  Some of the examples
we've talked about for except and always clauses are good clarifications
of
RFC 119, but do not alter its intent in any way.  For RFC 88 you tried,
and
at least mostly succeeded, in implementing the RFC in terms of Perl 5
capabilities.  While this is nice, I don't think it is a prerequisite
for specifying the semantics of a proposed syntax.

To me, an IMPLEMENTATION section delves into Perl internals, or specific
algorithms that can be cleverly applied to implement the result.  I
don't think the implementation of RFC 119 needs clever application of
algorithms, just the appropriate bytecode/code generation, and
implementation
of the specified semantics.  I said "Not much clue" simply because I've
no
in-depth knowledge of the internals of Perl.

> Also,
> for those who prefer the try/catch/finally approach, if we can establish
> these dualities then (1) they can do it their way, and (2) we can do it
> both ways (when circumstances make such a elocution clearer, rather
> than obfuscating it).

I agree that there are dualities.  I demonstrate many of the dualities
in RFC 119.  I've demonstrated additional dualities in this email.  So
far,
I still think RFC 119 describes a simpler yet more powerful set of
syntax
and semantics than RFC 88, although with some extensions you have gotten
closer, and with some more, you might yet achieve a total duality.

> Don't forget, we're building the basis for Perl 6's future idiomatic
> forms (especially if the core signals "failures" via exceptions).
> Let's give ourselves some room to work, shall we ;-?

Yep, that's the goal.  I'm extremely glad to see that you have
recognized that RFC 88 as written doesn't give an easy solution to some
easy
cases, and are willing to include some of these extensions.  However,
one way
of looking at that is that that is simply validating the approach
proposed
by RFC 119.

We still differ on whether exception handling (throw) should be unified
with failure handling (die)--your perl 5 implementation approach forces
you
to unify them--but that issue is actually orthogonal from creating a
functional, powerful, exception handling syntax and semantics.  If Larry
thinks they should be unified, I'll be content.  If Larry thinks they
should be separate, RFC 119 has no dependencies or conflicts with die
handling,
but the syntax could easily accomodate unification.

Another difference we have is that I think many of the try statement
modifiers you have created in RFC 88 to implement action-at-a-distance,
are extremely dangerous.  I think most of those modifiers you have added
because RFC 88 changes the basic semantics of how perl handles
exceptions, to
alleviate arguments against RFC 88.  But I don't have to use them (I
hope).  In those modifiers, you have some (dangerous) capabilities in
RFC 88
that are omitted from RFC 119.

> Yours, &c, Tony Olekshy

--
Glenn
=====
Even if you're on the right track,
you'll get run over if you just sit there.
                       -- Will Rogers
----- Stuff below this added by NetZero -----



Shop online without a credit card
http://www.rocketcash.com
RocketCash, a NetZero subsidiary

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