![]() |
Jupyter at Bryn Mawr College |
|
|
Public notebooks: /services/public/dblank / CS245 Programming Languages / 2014-Fall / Notes |
This discussion is about continuations. We have them built into our language, calc
, and also Scheme. Wouldn't it be cool if we could reach in and grab one?!
Yes! In Scheme, continuations are first-class objects.
For this discussion we need to remember a couple of functions from Scheme:
map
for-each
Recall that map
takes a function $f$ and applies it to each item in a list:
(map (lambda (s) (string-append s "!")) '("hello" "there" "little" "bunny"))
for-each
does the same, but doesn't return the list:
(for-each (lambda (s) (string-append s "!")) '("hello" "there" "little" "bunny"))
So, what is for-each good for? It is good for when you are interested in a function's side-effects rathern than its return value. For example, you might have a function that makes a robot move:
(define move
(lambda (motion)
(printf "Robot is moving ~s~%" motion)))
(for-each move '("backward" "forward" "up and down" "right" "left"))
Consider this simple function below:
(define f
(lambda (return)
(return 2)
3))
What will the following give?
(f (lambda (x) x))
In fact, it doesn't really matter what return does, it can only compute something, which is ignored.
But, if we pass f into call-with-current-continuation then something surprising happens:
(call-with-current-continuation f)
Let's look at a couple more examples:
(call-with-current-continuation
(lambda (return)
(begin
(print "One")
(return #t)
(print "Two")
#f)))
(call-with-current-continuation
(lambda (exit)
(map (lambda (x)
(if (eq? x 'blue)
(exit x)
(symbol->string x)))
'(this is a blue bunny in your yard))))
(call-with-current-continuation
(lambda (exit)
(map (lambda (x)
(if (eq? x 'blue)
(exit exit)
(symbol->string x)))
'(this is a blue bunny in your yard))))
(define generator
(lambda (lst)
;; Hand the next item from a-list to "return" or an end-of-list marker
(define control-state
(lambda (yield)
(for-each
(lambda (element)
(set! yield (call-with-current-continuation
(lambda (resume-here)
;; Grab the current continuation
(set! control-state resume-here)
(yield element)))))
lst)
(yield '())))
;; This is the actual generator, producing one item from a-list at a time
(define start-generator
(lambda ()
(call-with-current-continuation control-state)))
;; Return the generator
start-generator))
(define next
(generator '(0 1 2)))
(next) ;; 0
(next) ;; 1
(next) ;; 2
(next)
call-with-current-continuation
is a lot of typing! You can abbreviated it to call/cc
Consider this:
(call/cc (lambda (k) (k k)))
(define cont #f)
(begin
(print 1)
(print 2)
(set! cont (call/cc (lambda (k) (k k))))
(print 3)
(print 4)
)
(cont cont)
(cont cont)
(cont (lambda (i) i))
(cont (lambda (i) i))
(+ 1 (call/cc
(lambda (k)
(+ 2 (k 3)))))
(define redo #f)
(+ 1 (call/cc
(lambda (k)
(set! redo k)
(+ 2 (k 3)))))
(redo 42)
(redo 100)
; current-continuation : -> continuation
(define (current-continuation)
(call-with-current-continuation
(lambda (cc)
(cc cc))))
; fail-stack : list[continuation]
(define fail-stack '())
; fail : -> ...
(define (fail)
(if (not (pair? fail-stack))
(error fail "back-tracking stack exhausted!")
(begin
(let ((back-track-point (car fail-stack)))
(set! fail-stack (cdr fail-stack))
(back-track-point back-track-point)))))
; amb : list[a] -> a
(define (amb choices)
(let ((cc (current-continuation)))
(cond
((null? choices) (fail))
((pair? choices) (let ((choice (car choices)))
(set! choices (cdr choices))
(set! fail-stack (cons cc fail-stack))
choice)))))
; (assert condition) will cause
; condition to be true, and if there
; is no way to make it true, then
; it signals and error in the program.
(define (assert condition)
(if (not condition)
(fail)
#t))
We're looking for dimensions of a legal right triangle using the Pythagorean theorem:
$ a^2 + b^2 = c^2 $
And, we want the second side (b) to be the shorter one:
$ b < a $
(let ((a (amb (list 1 2 3 4 5 6 7)))
(b (amb (list 1 2 3 4 5 6 7)))
(c (amb (list 1 2 3 4 5 6 7))))
(assert (= (* c c) (+ (* a a) (* b b))))
(assert (< b a))
; Print out the answer:
(display (list a b c))
(newline))