* What are continuations?

At any given point of computation, you can say
that we are operating inside some context.

In Scheme all expressions have value, and the
context is usually what waits for a value to be
produced.

For example, when you enter this expression:

  (* (f (+ 1 2)) 3)

then at the point of evaluating (+ 1 2), the
context that waits for an answer is the f's
application, the context of (f (+ 1 2)) is the
multiplication, and the context of the
multiplication is the toplevel loop.

This is a little hard to understand, but a little
practice usually clarifies it.

This is a Scheme standard built-in with the lovely
name: call-with-current-continuation, but usually
call/cc as well.

(call/cc f) captures the current continuation as a
procedural object and transfers it to f.  This
means that we have ways to manage continuations as
normal Scheme objects.

The function that receives this continuation can
do whatever it wants with it - it can

1. apply it to a value, which is the same as returning
   the value from the call/cc context,

2. store it somewhere,

3. return some other value which will
   be the result of the call/cc expression.

Some examples to make things clear:

  ==> (+ 1 (call/cc (lambda (k) 2)) 3)
  6
  ==> (+ 1 (call/cc (lambda (k) (k 2))) 3)
  6
  ==> (+ 1 (call/cc (lambda (k) (+ (k 2) 66))) 3)
  6
  ==> (+ 1 (call/cc (lambda (k) (/ (k 2) 0))) 3)
  6
  ==> (define (p x) (echo '< x '>) x)
  ==> (+ 1
         (call/cc (lambda (k) (/ (k 2) 0)))
         (p 3))
  < 3 >
  6
  ==> (define k1 #f)
  ==> (+ 1
         (call/cc (lambda (k) (set! k1 k) 2))
         (p 3))
  < 3 >
  6
  ==> (k1 22)
  < 3 >
  26
  ==> (define x
        (call/cc (lambda (k) (set! k1 k) 1)))
  ==> x
  1
  ==> (k1 2)
  ==> x
  2

As demonstrated, we can do anything with
continuations, and we can capture toplevel
continuations as well.

MzScheme has a shortcut - instead of

  (call/cc (lambda (k) ...))

you can do

  (let/cc k ...)


--------------------------------------------------

* What do we get by using continuations?

Continuations were introduced into Scheme since
they go well with the spirit of the language - it
is a small feature that allows many things.

Basically, we can do all kinds of control
structures.

For example - non-local exit: what if we want an
abort function that will immediately return to the
repl?  We can define abort as the top level
continuation, but this is a little tricky...

  ==> (define abort (let/cc k k))
  ==> abort
  #<continuation>
  ==> (+ 1 2 (abort 3))
  ==> abort
  3

One way that we can solve this is by renaming that
as abort1, and then using this definition of
abort:

  (define (abort) (abort1 abort1))

But this is tricky, plus it doesn't allow us to
return some value.

The better way of solving this is:

  ==> (define abort #f)
  ==> (let/cc k (set! abort k))
  ==> (+ 1 2 (abort 3))
  3
  ==> (abort (+ 1 2 (abort 6)))
  6

or perhaps

(define abort #f)
(let ((x (let/cc k (set! abort k))))
  (when (number? x)
    (printf "aborting with return code ~a~n" x)))

==> (abort 3)
aborting with return code 3
==> (abort 4)
aborting with return code 4
==> 

Another example: lets say we want to get the
product of a list...

  (define (prod l)
    (if (null? l)
      1
      (* (head l) (prod (tail l)))))

This is inefficient when the list can contain
zeros.  So, we might do this:

  (define (prod l)
    (cond ((null? l) 1)
          ((zero? (head l)) 0)
          (else (* (head l) (prod (tail l))))))

which is better since it stops as soon as it
reaches a zero, but still - it returns this zero
which will get multiplied redundantly by previous
values...

So, we might do this:

  (define (prod l)
    (define (loop l a)
      (cond ((null? l) a)
            ((zero? (head l)) 0)
            (else (loop (tail l)
                        (* a (head l))))))
    (loop l 1))

but this is more complicated than the original
expression.  (Well, not to much in this case, but
other cases can be much worse.)

This has a natural solution with continuations:

  ==> (define (prod l)
        (cond ((null? l) 1)
              ((zero? (head l)) (abort 0))
              (else (* (head l)
                       (prod (tail l))))))
  ==> (prod '(1 2 3 4 0 5 6 7 8 9))
  0

or with a local escape:

  (define (prod l)
    (let/cc escape
      (define (prod l)
        (cond ((null? l) 1)
              ((zero? (head l)) (escape 0))
              (else (* (head l)
                       (prod (tail l))))))
      (prod l)))

Other variations on this are natural - for
example, search a binary tree and return one
element, do something with it and then retrieve
the next one.

Using continuations is also equivalent to the
power we get by using goto statements, for
example, we can directly translate this imaginary
code:

  (define (prod list)
    (let ((p 1))
      loop:
      (when (null? l) (goto out:))
      (set! p (* p (head list)))
      (set! list (tail list))
      (when (zero? p) (goto out:))
      (goto loop:)
      out:
      (echo "Product =" p)))

to something concrete:

  ==> (define (prod lst)
        (let ((p 1)
              (loop: #f))
          (let/cc out:
            (let/cc loop (set! loop: loop))
            (when (null? lst) (out:))
            (set! p (* p (head lst)))
            (set! lst (tail lst))
            (when (zero? p) (out:))
            (loop:))
          (echo "Product =" p)))

  ==> (prod '(1 2 3 4))
  24

  ==> (define a (list 0 1 2 3))
  ==> (set! a (append '(1 2 3 4 5 6) (append! a a)))
  ==> (prod a)
  0

[Note that this translation uses continuations
with no values, this is because we don't use their
values.]


--------------------------------------------------

* Cute things that can be implemented using
  continuations.

So one thing that we can do with abort is make it
a function that can also continue the aborted
computation: just before calling the toplevel
continuation, we save the local continuation and
use it to continue computation:

  ==> (define toplevel-k #f)
  ==> (let/cc k (set! toplevel-k k))
  ==> (define continue #f)
  ==> (define (abort msg)
        (let/cc cont
          (set! continue (lambda ()
                           (set! continue #f)
                           (cont #f)))
          (toplevel-k (list 'abort: msg))))
  ==> (define (fact n)
        (abort (list 'fact n))
        (if (<= n 0) 1 (* n (fact (sub1 n)))))
  ==> (fact 4)
  (abort: (fact 4))
  ==> (continue)
  (abort: (fact 3))
  ==> (continue)
  (abort: (fact 2))
  ==> (continue)
  (abort: (fact 1))
  ==> (continue)
  (abort: (fact 0))
  ==> (continue)
  24
  ==> (continue)
  ERROR - continue is now #f

One thing that we can do is change the break
handler (there is one in MzScheme) so it will call
this abort function - the result is that we will
be able to stop continuation, examine some global
variables and then resume.

This can be extended very easily to create
something realy nice, just give me continuations
and a queue!  (anyone ever seen McGyver?)

  ==> (define processes '())
  ==> (define (enqueue-process! p)
        (set! processes
              (append processes (list p))))
  ==> (define (dequeue-process!)
        (let ((p (head processes)))
          (set! processes (tail processes))
          p))
  ==> (define (more-processes?)
        (not (null? processes)))
  ==> (define (done)
        (if (more-processes?)
          ((dequeue-process!))
          (error "all done")))
  ==> (define (yield)
        (let/cc me
          (enqueue-process! me)
          (done)))
  ==> (define (fork f)
        (let/cc me
          (enqueue-process! me)
          (f)
          (done)))

And now:

  ==> (define (make-foo name n)
        (define (loop n)
          (echo "name:" name n)
          (yield)
          (sleep 1)
          (if (zero? n) (done) (loop (sub1 n))))
        (lambda () (loop n)))
  ==> (define (main)
        (fork (make-foo "foo" 6))
        (fork (make-foo "bar" 4))
        (fork (make-foo "baz" 2))
        (done))
  ==> (main)
  name: foo 6
  name: bar 4
  name: foo 5
  name: baz 2
  name: bar 3
  name: foo 4
  name: baz 1
  name: bar 2
  name: foo 3
  name: baz 0
  name: bar 1
  name: foo 2
  name: bar 0
  name: foo 1
  name: foo 0
  all done

This is a complete cooperative threading system in
23 lines of code!  And like McGyver, give me an
extra bubble gum and I'll do anything...  All you
need now is a timer function that will call yield
and you get preemptive processes.  (Unfortunately,
there is no such built-in mechanism, and although
it is not hard to implemet one, it will be too
much for here.)


--------------------------------------------------

* How do we get continuations?

So here we develop a pattern matcher for regular
expressions.  When we do this, we want to use more
general continuations - ones of different kinds.

Specifically, we want to have a do-match program
that will get:

1. a token list to be matched (a list of symbols),

2. a pattern to match against,

3. a success continuation,

4. a failure continuation.

We need continuations to do "backtracking" - when
we fail to match with some pattern, others might
be used on the same sequence.  For example, if out
expression is (A+AB)C, and we get an input of ABC,
then we first try to match A, succeed, but then we
try to match C and fail so we must backtrack to
the or (+) point and try the other branch.

So we define the success continuation as a
function that gets (1) the rest of the sequence
that needs to be parsed and (2) a failure
continuation that specifies what to do in case we
fail.

We also get a failure continuation which is a
function with no arguments that is used to abort
matching in case we fail.

[I know this doesn't sound too clear, looking at
the way this is implemented might help a bit but
it is hard to understand any way you look at it.]

;; -----------------------------------------------

;; define do-match as a generic funcion
(defgeneric (do-match xs pattern success fail))

;; the default case matches some token
(defmethod (do-match xs (p <symbol>) s f)
  ;; if we do have a token left
  (if (and (not (null? xs))
           ;; and it is the same one
           (eq? (head xs) p))
    ;; then we succeeded - use the success
    ;; continuation with what's left of the input
    ;; and the same failure continuation
    (s (tail xs) f)
    ;; otherwise we failed - use the failure
    ;; continuation to do this
    (f)))

;; we use objects to represent patterns
(defstruct <pattern>)

;; define the "or" pattern with two parts
(defstruct (<~or> <pattern>) p1 p2)
(define ~or make-~or)

(defmethod (do-match xs (p <~or>) s f)
  ;; basically, we try to match the given input
  (do-match xs
            ;; with the first part of the or
            (~or-p1 p)
            ;; and success is simply using the
            ;; given continuation
            s
            ;; but failure is different:
            (lambda ()
              ;; if we fail in the first part,
              ;; then the continuation in that
              ;; case tries to match the input
              ;; with the second part of the or
              ;; pattern, if *that* fails or
              ;; succeeds, then we use our
              ;; continuations
              (do-match xs (~or-p2 p) s f))))

;; define a sequence of two patterns
(defstruct (<~seq> <pattern>) p1 p2)
(define ~seq make-~seq)

(defmethod (do-match xs (p <~seq>) s f)
  ;; to match a sequence we first match the input
  (do-match xs
            ;; with the first part of the sequence
            (~seq-p1 p)
            ;; if we succeed
            (lambda (xs f)
              ;; then we match the second part
              ;; using the same success
              ;; continuation we received but
              ;; using the failure continuation
              ;; that we were given now (note that
              ;; xs is whats left now)
              (do-match xs (~seq-p2 p) s f))
            ;; if we fail with matching the first
            ;; part, we simply abort
            f))

;; a structure that represents repeated patterns,
;; zero ot more times
(defstruct (<~rep> <pattern>) p)
(define ~rep make-~rep)

(defmethod (do-match xs (p <~rep>) s f)
  ;; to much the repeated sequence P... we try to
  ;; match the input with
  (do-match xs
            ;; a sequence of P and P... itself,
            ;; note that the order here is
            ;; important - we must try P because
            ;; otherwise we end in an infinite
            ;; loop
            (~seq (~rep-p p) p)
            ;; success is nothing special
            s
            ;; but if that fail, we can say that
            ;; this is actually success if we
            ;; don't take anything from the input
            ;; since it means *zero* or more times
            (lambda () (s xs f))))

;; this is a utility function that turns a string
;; into a token (symbol) list
(define (string->symbols str)
  (map string->symbol
       (map string (string->list str))))

;; this is the entry point
(define (match x pattern)
  ;; it uses do-match on the input sequence
  ;; followed by a $ token
  (do-match (append (string->symbols x) '($))
            ;; and adds a terminating $ to the
            ;; patern as well, this will force it
            ;; to get an exact match
            (~seq pattern '$)
            ;; this is the toplevel success
            ;; continuation
            (lambda (xs f) #t)
            ;; and the toplevel failure
            (lambda () #f)))

;; try some trivial tests
(match "a" 'a)
(match "a" (~or 'a 'b))
(match "a" (~seq 'a 'b))
(match "ab" (~seq 'a 'b))

(define ab* (~rep (~seq 'a 'b)))
(match "a" ab*)
(match "ab" ab*)
(match "aba" ab*)
(match "abab" ab*)
(match "" ab*)

;; this is the example we talked about earlier
(match "abc" (~seq (~or 'a (~seq 'a 'b)) 'c))

;; and this is a harder example
(define foo (~rep (~or (~seq 'a 'b)
                       (~seq 'a (~seq 'b 'a)))))
(match "" foo)
(match "ab" foo)
(match "aba" foo)
(match "ababa" foo)
(match "abaaba" foo)
(match "abaaaba" foo)

;; -----------------------------------------------

What we did here is called programming with
continuation passing style (CPS).  We basically
created our own continuations manually and used
them.

Usually the built in call/cc (or let/cc) are
enough, but this case required a special, designer
continuations.

We can actually convert any code so it builds its
own continuations, by something that is called CPS
transformation.  We basically turn every
expression in the language into a function that
takes a continuation, evaluates something, and
sends the result to this continuation.

Here is a quick set of rules to do CPS
transformation on very simple Scheme expressions
(this is the Lambda Calculus subset):

  x'  ==
    (lambda (k) (k x))

  (lambda (x) E)'  ==
    (lambda (k) (k (lambda (x) E')))

  (E1 E2)'  ==
    (lambda (k)
      (E1' (lambda (x1)
             (E2' (lambda (x2) (k (x1 x2)))))))

There are several important notes here:

* CPS transformation return expressions that are
  always tail recursive.

* This was also the case with the matcher above.

* If you'll try to follow the evaluation of a CPS
  transformed code, you'll see that the
  continuation keeps growing when there are things
  to remember on the evaluation stack.

* We can make slight variations on the above to
  get different evaluations - right to left, or
  even lazy evaluation.

So two major points that continuations are related
to are:

1. The evaluation stack - continuations actually
   collect stuff to remember the same way that a
   CPU uses stack.  This is also the reason why
   implementations of continuations are usually
   done by taking "snapshots" of the run-time
   stack.

2. Continuations can be used to store and use
   future computations, so they give us a way to
   manage nondeterminism conveniently, like the
   above matcher.  (There is a very deep logical
   background to this.)