[comp.lang.scheme] error objects, conditions, and unwind protection

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.