On Sat, Jun 24, 2006 at 11:18:41PM -0400, Bob Rogers wrote:
>    From: Chip Salzenberg <[EMAIL PROTECTED]>
>    Date: Tue, 20 Jun 2006 20:59:45 -0700
> 
>    WRT exception handling, I think the lisp condition/handler model is a good
>    starting point.  It's simple enough to explain and use, and static models
>    can easily be implemented in terms of it.
> 
> Excellent; I'm sure you won't be surprised to learn that I agree.  ;-}

Consensus is easy to achieve when one party is obviously correct.  :-)

>    But I really don't like one thing about the CL handler model: it conflates
>    non-local transfers of control with "this exception is now handled".
> 
> FWIW, some pre-ANSI implementations did have a mechanism for marking a
> condition as having being handled [...]

No, nothing like a 'handled' flag in the condition.  It's just a question of
how Parrot should be informed that an exception is caught ... whether all
languages, rather than CL, should make non-local control flow semantically
significant.

Consider: from Parrot's low-level POV, how could Parrot notice when it's
leaving the dynamic context of a condition handler specifically, so as to
change its internal state of "There's a live condition that's in the process
of being handled" into "Ah, all done then, the handling is over"?  The most
obvious answer involves extra processing checking for the situation of
exception-handling, but that would slow down every continuation invocation.

Fortunately, we don't have to go there.  To quote myself:

>    If dynamic-wind is implemented (see below), it seems to me that a CL
>    compiler could wrap each handler in a dynamic scope in such a way as to 
> trap
>    a non-local transfer distinctly from a return, and in the former case,
>    automatically invoke the hypothetical Parrot "caught" opcode.  So CL users
>    get full CL semantics, and everybody gets a faster continuation.invoke()
>    operation.

In other words: Dymamic-wind processing will be required in every
continuation invocation.  Therefore, if Lisp-style condition handling is
build on D-W, CL will *not* require a special flag/check/slowdown.  In fact,
CL would not be alone in this: most of the exception code for most languages
could be written in PIR.  That's not only nice in itself, but it's a very
good sign for the power of PIR.

Quoting you out of order:

>    I even intend to use continations to implement THROW and CATCH; I
> just won't be able to expose them to users via standard Lisp constructs.
> So, yes, I could install the equivalent of an UNDO block around the Lisp
> code that does whatever Parrot maintenance is required on the Parrot
> exception object (which, it now occurs to me, may need to be distinct
> from the Lisp condition object).  But would I really need to do anything
> here?  If an exception is caught by Lisp, why would Parrot even need to
> know?  S04 seems to require a great deal of bookkeeping for unhandled
> exceptions, but would that necessarily impact Lisp handlers?

It's just a little hack, no big deal.  Imagine this scenario:

  1. Parrot has exceptions
  2. Parrot requires handlers to mark exceptions handled with a "caught"
     opcode
  3. Parrot has dynamic-wind

Given:

  (handler-case (signal condition)
     (printer-on-fire () YOUR_FORM_HERE))

Your CL compiler would replace YOUR_FORM_HERE with the equivalent of this,
written in pidgin Scheme:

   (let ((handled #t))
     (dynamic-wind
       ;; entry thunk (none)
       nil

       ;; body thunk (CL handler code goes here)
       (lambda ()
         YOUR_FORM_HERE
         (set! handled #f))

       ;; departure thunk
       (lambda ()
         (when handled            ;; only a non-local transfer could avoid 
(set! ... #f)
           (parrot-emit "caught") ;; here's where you tell Parrot the exception 
is handled
           (set! handled #f)))))  ;; ... but we only want to do so once per 
exception

I suspect that the lexical nature of the 'handled' flag may not match the
interpreter-wide-dynamic nature of the signal stack, leading to incorrect
results with nested signals.  But with that caveat, I think this would work.

Anyway, the point of this whole dance is to implement the CL semantics,
which require you to *detect* and *take special action on* the handler body
making a non-local transfer out of the dynamic scope ... something which you
want, since non-local transfers are semantically significant in the
definition of CL condition handlers (and only CL condition handlers :-)).

----

Moving on: I may have missed some of the implications of what I'm not
quoting, but this:

> Such an implementation is truly and utterly stackless, which means that
> dynamic-wind needs to keep its own stack explicitly, and similarly for
> dynamic binding (which, IIUC, is generally implemented in terms of
> dynamic-wind).

... actually describes Parrot, present and future.  Parrot doesn't need to
recurse in C to invoke continuations or closures (even if maybe it does in
some cases (weasel word alert)).  And my intended implementation of
dynamic-wind actually does require its own stack, separate from the current
control stack.  Condition handlers, if they even end up with significant
core support, will also have their own stack, I think.

>    One question about push_dynscope, though:  Is the
> sub_to_call_when_scope_is_finally_inaccessible called when the
> Parrot_Context is reclaimed?  If so, why is that needed?

No.  It's called when the _dynamic_context_ is no longer accessible, i.e. when
these two conditions are met:

  1. the flow of control leaves the dynamic scope (and the 'leave' thunk, if
     any is called)
  *AND*
  2. there are no live continuations that, if invoked, would lead back into the
     same dynamic scope

The third thunk is the "I'm leaving the hotel room and checking out" thunk,
as opposed to the second thunk, which is the "I'm leaving the hotel room but
I *might* be back after dinner" thunk.

>    And getting back to exceptions, I'm seeing something that's pretty much 
> like
>    the CL model, where the 'push_eh' opcode takes a _closure_, and the list of
>    handlers is its own array in the interpreter, not in the generic control
>    stack, and which is called at 'throw' time in the dynamic context of the
>    'throw'.  For conventional static languages like Perl 6 (:-)), the handler
>    would pretty much report that the exception was handled (e.g. with a
>    'caught' opcode) and then invoke a continuation which had been taken by the
>    Perl 6 compiler to point to the 'catch' code . . .
> 
> That sounds good to me.  The two-stack model (handler and control) still
> has the problem [2] of keeping them in sync when unwinding, but I assume
> that will be in your new PDD23 version.

Every continuation invocation would automatically switch to the correct
exception stack, presumably using the same (or very similar) mechanism as
dynamic-wind.  Heck, we could just avoid any Parrot core support for
exceptions at all, and do it with a standard library and dynamic-wind.  That
would allow us maximum flexibility, wouldn't it?  Hm.

>    It seems so obvious that I'd suggest that's how it works, except for some
>    reason I can't fathom, CL doesn't support continuations...?
> 
> [...]  some half-dozen vendors were involved, and they all had major
> investments in traditional stack-based implementations. [...]

I know ANSI C, and I know POSIX.3.  Say no more.  :-P
-- 
Chip Salzenberg <[EMAIL PROTECTED]>

Reply via email to