barak+@cs.cmu.edu (Barak Pearlmutter) (05/02/91)
In Oaklisp we addressed the dynamic issues under discussion here: condition handling and unwind protection. Error System We adopted a CommonLisp-like condition system. Using Oaklisp's object-oriented facilities, it was easy to construct a multiple hierarchy of condition types (PROCEEDABLE-ERROR, FS-ERROR, PROCEEDABLE-FS-ERROR, ERROR-OPENING, ERROR-OPENING-APPEND...), instantiated to form objects that represent exceptional events. (Fluid variables provide a natural implementation mechanism for the condition handlers, giving proper interaction with continuations and unwind-protect.) The condition system permits desirable behaviors, like failure to open a file throwing the user into the debugger with options to resume the computation in various ways, such as retrying or trying a different filename. But this behavior is just the default, and can be dynamically overridden by the caller of OPEN, or the caller of the caller to OPEN, etc, as is done in, say, the batch file compiler. This is convenient and extensible, and allows naive users to get reasonable behavior without doing anything. But without the condition hierarchy it would be a mess, so I doubt it's time to standardize. Unwind protection It is nice to have files closed and popup windows disappear even when you use the debugger to zap a computation. This is an unwind protection system's job. For database applications such systems have been implemented for C using somewhat portable C code, so their importance and ease of implementation (in the absence of CALL/CC) should be clear. Oaklisp's unwind protection system is reminiscent of CommonLisp's, but because CALL/CC allows control to dynamically enter a context multiple times, there are both unwind forms for exits and rewind forms for entries. Calling a continuation unwinds down to the closest common context in the execution tree, and then winds up to the target context. A further generalization allows two different wind and unwind actions, for "normal" vs "abnormal" entries and exits. I don't know what "the well-known difficulty of defining UNWIND-PROTECT in the presence of firstclass continuations" is. We didn't have any trouble. Wind-protect looks pretty Schemey on the face of it, but it is disquieting that if incorporated into a Scheme with multiprocessing and futures, unwind protection loses it's intuitive properties. Fun example % oaklisp Warm booting ...... Welcome to Oaklisp 1.1 - No Warranty expressed or implied. Refer to the source for information. Copyright (C) 1989, 1988, 1987 Barak Pearlmutter and Kevin Lang. Oaklisp evaluation loop. Active handlers: 0: Return to top level. > (set! (fluid fancy-references) #t) ;Make anonymous objects print their names. #T > (set! hole-count 0) Warning: Installing HOLE-COUNT in #<Locale SYSTEM-LOCALE 658>. 0 > (define (in-hole thing-to-do) (let ((hole-number (set! hole-count (+ hole-count 1)))) (funny-wind-protect (format #t "Drilling hole #~D.~%" hole-number) (format #t "Slipping back into #~D.~%" hole-number) (thing-to-do) (format #t "Pulling out of #~D.~%" hole-number) (format #t "Yanking out of #~D!~%" hole-number)))) Variables installed in #<Locale SYSTEM-LOCALE 658>: IN-HOLE. #<Op IN-HOLE 1021> > (in-hole (lambda () 'okay)) Drilling hole #1. Pulling out of #1. OKAY > (block (call/cc (lambda (k) (set! foo k))) (format #t "Jumped to top level.~%")) Variables installed in #<Locale SYSTEM-LOCALE 658>: FOO. Jumped to top level. () > (in-hole (lambda () (foo 'oops))) Drilling hole #2. Yanking out of #2! Jumped to top level. () > (in-hole (lambda () (call/cc (lambda (k) (set! bar k) 'okay)))) Variables installed in #<Locale SYSTEM-LOCALE 658>: BAR. Drilling hole #3. Pulling out of #3. OKAY > (bar 'ookay) Slipping back into #3. Pulling out of #3. OOKAY > (in-hole (lambda () (bar 'okaay))) Drilling hole #4. Yanking out of #4! Slipping back into #3. Pulling out of #3. OKAAY > (in-hole (lambda () (in-hole (lambda () (bar 'ookaay))))) Drilling hole #5. Drilling hole #6. Yanking out of #6! Yanking out of #5! Slipping back into #3. Pulling out of #3. OOKAAY > (in-hole (lambda () (car 'foo))) Drilling hole #7. Error: no method for #<LocatableOp CAR 1024> with 1 arg: FOO. Oaklisp evaluation loop. Active handlers: 0: Return to top level. 1: Retry applying #<LocatableOp CAR 1024> to (FOO). 2: Return a value from the call to #<LocatableOp CAR 1024>. 3: Return to debugger level 1. >> (ret 0) Invoking handler "Return to top level." Yanking out of #7! > (exit) Oaklisp stopped itself... Barak Pearlmutter Department of Psychology P.O. Box 11A Yale Station New Haven, CT 06520-7447 barak@james.psych.yale.edu -- --Barak.