Here are some examples of the behavior of letcc:

In general, when a letcc expression is evaluated, it turns its current context (as in, ``complete textual context'') into a procedural object. This procedural object is also known as a continuation object. When a continuation object is applied, it forces the evaluator to remove the current evaluation context and to re-create the context of the original letcc expression, filled with the value of its argument. Like procedures, continuation objects are first-class values, which means they can be stored in data structures or tested by predicates.

In essence, letcc takes the current activation record and program counter at the point of invoking letcc and encapsulates them as a procedure waiting for a value to be returned. The body of the letcc is evaluated in the current environment extended by the binding of the letcc continuation name to the encapsulated procedure -- just as the body of a conventional let would be evaluated in an environment extending by the bindings specified in the definitions at the head of the let.

Now we understand the semantics of letcc, but how do we use it to write programs? We need to also understand its pragmatics. For instance, letcc can be used for exception handling.

Example:

Let us write a procedure, PiHelp, that computes the product of a list of digits. The data definition looks like this:

lod ::= null | (cons [0-9] lod)

Our procedure might look like this:

(define PiHelp
  (lambda (l)
    (cond
      ((null? l) 1)
      (else (* (car l) (PiHelp (cdr l)))))))

However, suppose it is possible that we can get an invalid digit (in the range [a-f]); if we do, we want the result of invoking PiHelp to be false. We can add the following clause within the cond statement,

      ((bad? (car l)) (Xit #f))

where Xit is some unused identifier.

We use the following recipe for constructing such programs:

  • From the data description, recognize the exceptional data.
  • In the corresponding code, add the call (Xit V) (where V represents the value to be returned in an exceptional situation) where Xit is some new identifier.
  • Bind Xit by making it a parameter in the argument list to the procedure.
  • Change all calls to that procedure to reflect the new arity. Since we don't know what Xit, we simply pass it along at all these call sites.
  • Write a wrapper function to the desired procedure which takes the desired number of arguments. For instance,
        (define Pi
          (lambda (l)
            (letcc Abort (PiHelp l Abort))))
        

If we pass this new procedure exceptional data, we get

    (Pi '(1 2 b))
==> (letcc Abort (PiHelp '(1 2 b) XXX))
==> (letcc Abort (* 1 (*2 (PiHelp #f))))
==> #f

as desired.

Exercises:

  • Instead of creating a separate procedure PiHelp, could we have written (define Pi (letcc Xit ...))?
  • Along similar lines, could we have written (define Pi (lambda ... (letcc Xit ...))) instead?
  • Could we have hidden the code for PiHelp inside that of Pi using local?
  • If we did this, would we still need to pass the continuation around?

Modeling Simple Control Constructs

There are numerous control constructs that we can add to LC. Some of these are:

Here is the core of an interpreter that implements # and raise. This version of # takes a body and a handler, as outlined above. The handler takes one argument, which is the value ``thrown'' by raise.

(define MEval/ec
  (lambda (M env Exit)
    (cond
      ...
      ((raise? M)
	(Exit (MEval/ec (raise-expr M) env Exit)))
      ((#? M)
	((letcc new-Exit
	   (lambda ()
	     (MEval/ec (#-body M) env
	       (lambda (raised-value)
		 (new-Exit (lambda ()
			     (MApply/ec
			       (MEval/ec (#-handler M) env Exit)
			       raised-value Exit))))))))))))

(Note that all calls to the former MEval will now have to call MEval/ec instead, passing on the Exit handler unchanged; only # installs new handlers. MApply/ec is similarly modified.)

Summary

At this point, we will conclude our study of meta-interpreters. We have thus far covered the following:

It would be worthwhile to note in passing some of the topics that we did not cover but which could be studied with the same methodology:


Back to course website