# CSE 341: Scheme: Continuations and exceptions

## Continuations

An expression's continuation is "the computation that will receive the result of that expression". For example, in the expression

`(+ 4 (+ 1 2))`

the result of `(+ 1 2)` will be added to 4. The addition to 4 is that expression's continuation. If we wanted to represent the continuation of `(+ 1 2)`, we might write:

`(lambda (v) (+ 4 v))`

That is, the continuation of `(+ 1 2)` takes a value, and adds four to that value.

Every expression has an implicit continuation. In most languages, continuations are hidden under the covers and not accessible at all. In Scheme, the current continuation can be reified as a function by using the built-in function `call-with-current-continuation`, or `call/cc` for short.

`(call/cc expr)` does the following:

1. Captures the current continuation.
2. Constructs a function `C` that takes one argument, and applies the current continuation with that argument value.
3. Passes this function as an argument to `expr` --- i.e., it invokes ```(expr C)```.
4. Returns the result of evaluating ```(expr C)```, unless `expr` calls `C`, in which case the value that is passed to `C` is returned.

Here is an example:

```(+ 4 (call/cc
(lambda (cont) (cont (+ 1 2)))))```

This performs exactly the same computation as ```(+ 4 (+ 1 2))```. However, it uses `call/cc` to capture the current continuation, and then passes the result of evaluating `(+ 1 2)` directly to that continuation. Another, roughly equivalent way of writing the above is as follows:

```((lambda (cont) (cont (+ 1 2)))
(lambda (v) (+ 4 v))```

### Continuations are first class

Continuations are first-class values, and so do not have to be called immediately:

```> (define x ())
> (define (put-cont-in-x cont) (set! x cont))
> (define print-line (lambda (x) (display x) (newline)))

>(print-line (call/cc put-cont-in-x))
#<void>
> (x "hi!")
"hi"```

What's going on here? The current continuation at `(call/cc put-cont-in-x)` is the application of `print-line`. By the behavior of `call/cc`, `put-cont-in-x` therefore receives a continuation that takes its argument and passes it to `print-line`. `put-cont-in-x` stores this continuation as the value bound to `x`. Finally, when we invoke `x`, the argument list gets passed to the continuation we captured --- which passes its argument to `print-line`, which then prints the value followed by a newline.

### Continuations for "early exit" from nested evaluation

A less trivial use of `call/cc` follows. Consider a function that searches a list a value satisfying some predicate. The naive implementation is as follows:

```(define naive-find
(lambda (pred x)
(cond ((null? x) ())
((pred (car x)) (car x))
(else (naive-find pred (cdr x))))))
```

Suppose that Scheme did not perform proper tail calls. Then, this function would have to return from each recursive call when the recursion terminated. The following function fixes this problem by passing the final result directly to an earlier continuation:

```(define find
(lambda (pred aList)
(call/cc (lambda (cont)
(letrec ((helper (lambda (x)
(cond ((null? x) (cont ()))
((pred (car x)) (cont (car x)))
(else (helper pred (cdr x)))))))
(helper pred aList))))))```

This variation of `find` uses `call/cc` to bind the continuation to `cont`. Then, when we reach either terminating case (`(null? x)` or `(pred (car x))`) of the recursion, we invoke `cont` with some value (`()` or `x`). This skips all the recursive calls, and returns the value directly to a continuation. We can prove this by executing a slightly modified version of the naive and continuation-calling find functions:

```(define printing-naive-find
(lambda (pred x)
(cond ((null? x)      ())
((pred (car x)) (car x))
(else (let ((retval (printing-naive-find pred (cdr x))))
(display "returning from recursive call at: ")
(display (car x))
(newline)
retval)))))

(define printing-find
(lambda (pred x)
(call/cc
(lambda (cont)
(letrec ((helper
(lambda (pred x)
(cond ((null? x)      (cont ()))
((pred (car x)) (cont (car x)))
(else (let ((retval (helper pred (cdr x))))
(display "returning from call at: ")
(display (car x))
(newline)
retval))))))
(helper pred x))))))```

The `printing-naive-find` prints values "on the way back" from the last recursive call. The `printing-find` version does not, because the continuation is captured before entering the recursive function. The result is passed directly to the caller of the outermost `lambda`.

In the examples above, the `naive-find` function is tail-recursive. There's no need to use the continuation-invoking optimization to pass the end result directly to caller of the original invocation. However, for non-tail-recursive functions, the use of continuations can make returning from the "last call" a constant-time rather than linear-time operation.

The ability of `call/cc` to break out of deeply nested evaluation is often useful when returning errors. Consider the following function:

```(define divide-or-error
(lambda (aList divisorList errorValue)
(cond ((null? aList) ())
((= (car divisorList) 0) errorValue)
(else (let ((result (divide-or-error
(cdr aList) (cdr divisorList) errorValue)))
(if (= result errorValue)
errorValue
(cons (/ (car aList) (car divisorList))
result)))))))```

This function divides each value in `aList` by the corresponding value in `divisorList`, and returns a list of the answers; however, if there is any zero value in `divisorList`, it returns `errorValue` instead. Again, returning from each recursive call individually is a waste of time when we only want to pass `errorValue` back to the original caller. By capturing the initial continuation, we can return directly:

```(define divide-or-error2
(lambda (aList divisorList errorValue)
(call/cc (lambda (cont)
(define helper
(lambda (values divisors)
(cond ((null? values) ())
((= (car divisors) 0) (cont errorValue))
(else (cons (/ (car values) (car divisors))
(helper (cdr values) (cdr divisors)))))))
(helper aList divisorList)))))```

Notice that we no longer need to test the result of the recursive call for `errorValue`: when a zero is detected in `divisors`, it will be returned directly to the caller of the outermost `lambda`.

### The `dynamic-wind` function

Sometimes, we want to make sure that a function gets executed regardless of whether a continuation "bypasses" part of the currently evaluating expression. The `dynamic-wind` function takes three arguments, each of which must be a no-argument function value:

`(dynamic-wind inFn bodyFn outFn)`

The semantics of `dynamic-wind` are as follows:

• Call `bodyFn` and return its value.
• Whenever `bodyFn` is entered, either during the current evaluation or throughe valuation of any continuation captured inside `bodyFn`, `inFn` will be called beforehand.
• Whenever `bodyFn` is exited, either during the current evaluation or through evaluation of any continuation captured inside `bodyFn`, `outFn` will be called afterwards.

In the following example, `open-input-file` opens an input port (much like a file handle in other languages) which must be closed afterwards with `close-input-port`:

```(define (safe-process input-file process-fn)
(let ((p (open-input-file input-file)))
(dynamic-wind
(lambda () #f)
(lambda () (process-fn p))
(lambda ()
(begin (display "closing input port...")
(newline)
(close-input-port p))))))```

The in-function does nothing; the out-function closes the input port; and the body function applies the `process-fn` to the opened input port. Regardless of how `process-fn` exists, the out-function will always be called. (Typical usage of the out-function parameter to `dynamic-wind` closely resemble that of `finally` in Java.)

Now, let us apply `safe-process` to a function that exits prematurely:

```;; Define a processing function that always exits to toplevel.
(define exit-to-toplevel 'dummy)
(call/cc (lambda (cont) (set! toplevel-exit cont)))
(define (process p) (toplevel-exit))

;; Observe that dynamic-wind still executes the code which
;; closes the input port.
(safe-process "lecture14-dynamic-wind.ss" process)
; displays: "closing input port..."
```

## Exceptions

In ML and most other languages, raising and handling of exception are special constructs in the language. In Scheme, we can define exceptions as an ordinary library, using continuations and `dynamic-wind`.

### Desired usage

It is easiest to understand how to define exceptions backwards, by going from use to definition. Here's how we'd like to use the `raise` function:

```;; Function that raises an exception.
(define (find-or-raise pred x)
(cond ((null? x) (raise '(empty "No such element in list.")))
((pred (car x)) (car x))
(else (find-or-raise pred (cdr x)))))```

Notice that we're representing the exception value as a list, where the first element of the list is a symbol naming the exception and the remaining values in the list are values to be carried with the exception.

Here's how we might like to use the `handle` function:

```(define gt0 (lambda x (> x 0))
(define find-result
(handle 'empty
(lambda () (find-or-raise gt0 '(-1 -2 -3 -4)))
(lambda (anExn) -1)))```

That is, we would like the arguments to be

1. a symbol naming the exception to be handled;
2. a no-argument function that evaluates some body expression; and
3. a single-argument function that takes an exception, and produces a value to be returned when that exception is raised.

(Notice that this definition of `handle` only adds one handler per expression. It's no fundamentally harder to have a `handle` that takes multiple handlers and tries them in sequence, so for simplicity we'll stick with this.)

Taken together, the uses of `raise` and `handle` above should give roughly the behavior given by the ML definitions:

```exception Empty of string;
fun findOrRaise (pred, x) =
if null x then raise Empty "No such element in list"
else if pred (hd x) then x
else findOrRaise pred (tl x)

val gt0 = fn x => x > 0;
val findResult =
findOrRaise gt0 [-1, -2, -3, -4]
handle Empty msg => -1```

Now, we just need to figure out how to implement `raise` and `handle`...

#### The handler stack

At any given point in evaluation, there is a stack of "active" handlers. When an exception is raised, we must pop handlers off the stack until we reach one that can handle the current exception. Implementing a global stack as a list is easy enough:

```(define handler-stack ())

(define (push-handler handler)
(set! handler-stack (cons handler handler-stack)))

(define (pop-handler)
(if (not (null? handler-stack))
(let ((top (car handler-stack)))
(begin
(set! handler-stack (cdr handler-stack))
top))))```

We'll postpone the describing of the handlers themselves until we see the `handler` function.

#### Unhandled exceptions

When we get an unhandled exception, we must exit to top-level and print a message. In order to do this, we must capture a top-level continuation, which we'll invoke whenever we raise an exception with an empty handler stack:

```(define exit-to-toplevel 'dummy)
(call/cc (lambda (cont) (set! exit-to-toplevel cont)))```

Notice that anyone who evaluates `(exit-to-toplevel)` will now escape to toplevel, halting evaluation of any enclosing expression. (To prove this to yourself, write a deeply-nested expression that applies `exit-to-toplevel`.)

#### The `raise` function

The handler stack is a list. When an exception is `raise`d, there are two cases:

1. The handler stack is empty. In this case, we must print a message and exit to top-level.
2. The handler stack is non-empty. In this case, we must test whether this handler is suitable. This has two subcases:
1. If the handler is unsuitable, re-raise the exception.
2. If the handler is suitable, we must evaluate the handler function and pass its result to the continuation of the `handle` expression.

The trickiest part of this is case 2(a). Recall our sample use of `handle` for the `find-or-raise` invocation above: when the handler for `'empty` is invoked, we must return `-1` to the whole `handle` expression's continuation (in this case, the execution of the `define` for `find-result`).

So, in order to define `raise`, we must have the following three things available in the handler:

• The exception name.
• The continuation of the `handle` expression.
• The handler function.

We will therefore represent a handler as a three-element list `(exn-name handler-cont handler-fn)`. Assuming that `handle` pushed these things onto the stack, `raise` is defined as follows:

```(define (raise anExn)
(if (null? handler-stack)
(begin
(map display '("uncaught exception: " anExn))
(exit-to-toplevel))
;; else
(let* ((exn-name (car anExn))
(handler (pop-handler))
(handler-exn-name (car handler)))
(if (eq? exn-name handler-exn-name)
(handler-cont (handler-fn anExn)))
;; else
(raise anExn)))))```

#### The `handle` function

Finally, we must define `handle`. The `handle` function must take three arguments: an exception name, a body function, and a handler function. It must evaluate the body function, but during the evaluation of the body we must be prepared to handle any exception raised in the handler --- i.e., we must add a handler value of the form ```(exn-name handler-cont handler-fn)``` to the handler stack.

We implement this using `call/cc` and `dynamic-wind`:

```(define (handle exnName bodyFn handlerFn)
(call/cc (lambda (cont)
(dynamic-wind
(lambda () (push-handler (list exnName cont handlerFn)))
bodyFn
(lambda () (pop-handler))))))```

Notice that we use the out-function to pop the handler when `bodyFn` has completed execution.

## Suggested exercises

1. From the course web, download the code (lecture-15-exceptions.ss) that implements exceptions. Insert `display` statements at various points in the code to see when they get executed, and what the contents of `handler-stack` are.
2. Draw diagrams of the heap, including the handler stack, at each of the following phases of the execution of the `(define find-result ...)` example above:
1. Immediately after the call to `handle`.
2. Immediately after the `(call/cc` (lambda (cont) ... inside `handle`. Represent a continuation as cell with an arrow pointing to the point in the code where the continuation's argument will be "sent".
3. Inside the call to `dynamic-wind` inside `handle`, but before execution of `bodyFn`.
4. Inside the call to `bodyFn` (which will be `find-or-raise`)
5. Immediately inside the call to `raise`.
6. After the call to `(handler-fn anExn)` inside `raise`.