Invent

Gems in Scheme





One sometimes hears that call-with-current-continuation is useless or pointless. Such things have come from the Python developers, and while Ruby 1.8 had a call/cc method, it’s not clear if it will survive in the next major release, allegedly because it’s “useless” or “too expensive”.

In fact, I tend to think that in fact, these developers simply don’t understand what call/cc is about or why it is valuable. So I’ll show some examples.

First, of course, call/cc can be used to set up a dynamic exit. For example, call/cc is used in the following situation all the time:

The first procedure is called as such:

And then the do-something and do-something-else procedures can, if they need to, call raise-error. This will invoke the continuation captured by error-protect, and cause the error-protect form to immediately exit.

Of course, languages worth using already have exception-raising facilities. But it’s worth noting that, in principle, exception-raising is already using half the facility of call/cc. However, exception facilities are generally tuned carefully to errors and exceptional conditions. While the above procedures are named error-protect and raise-error, they could just as easily be used for a computation which can return a result as soon as it’s found.

Suppose we want to traverse a list, and return the first element which matches a predicate. (This procedure is specified in SRFI-1, by the way.)

This depends on the recursive step being implemented as properly tail-recursive, and also requires us to know the way into the data structure. Suppose we don’t have car and cdr–that is, suppose we are dealing not with a list, but with an abstract data type, and the interface only provides an iterator. That is, instead of a list, we have a table, with an iterator function (each proc table) which calls proc for each element of table. How can we do a find across that? Well, we have to run the iterator, store the matching value, and return it when we’re done:

But this has several problems. First, it requires us to walk through the entire table even after we’ve found our match. And it’s horrible, relying on a side-effect despite it not really being necessary. With call/cc, this problem goes away:

See how this works? The iterator “each” runs through the table one element at a time. Eventually if the predicate succeeds, we invoke the continuation, and the call-with-current-continuation immediately returns the matching element. If the predicate never returns true, then the call to each returns normally, and we return #f.

Again, this could have been done with exceptions, of course. But notice that exceptions are generally tuned to exceptional conditions and errors. There is nothing exceptional in find-in-table encountering a matching element. Also, notice that here we do not need the global variable that we had with error-protect and raise-error: the continuation c is lexically available only in the
indicated code, and this gives us significant programming clarity. We are *guaranteed* that nothing else can raise this exception, and not because we had to name it cleverly, or something else, but because it is simply a local variable not accessible elsewhere.

Lisp programmers and others are often familiar with catch and throw. These calls, like most exception handling facilities, use global identifiers to express what is caught and thrown. By contrast, find-in-table has none of those shortcomings, and cannot have such accidental collisions with other code.

In future installments, I’ll discuss the use of call/cc where continuations hang around longer than the invocation which produced them, and how call/cc can be implemented with great efficiency.

Leave a Reply

Your email address will not be published. Required fields are marked *

On Facebook

Recent Tweets

Follow Us

Scroll to top