I think I got what a continuations is (in general), but I can't understand how it is used in Scheme.
Consider this example (from wikipedia call/cc)
(define (f return)
(return 2)
3)
(display (call/cc f)) ;=> 2
I cannot understand why:
the continuation is implicit?right?
How is the continuation in this case?
The continuation is the "rest of the computation" that remains to be executed. In your particular example, you could think of this as being (display []) where [] is a hole to be plugged with a value. That is, at the point that call/cc is invoked, what remains to be done is the call to display.
What call/cc does is take this continuation and puts it in a special value that can be applied like a function. It passes this value to its argument (here f). In f, the continuation is bound to return. So (return 2) will basically plug 2 into the continuation, i.e., (display 2).
I don't think this example is actually very helpful, so I think you should read PLAI if you're interested in learning more about continuations (see Part VII). Another good source is these lecture notes by Dan Friedman.
Related
Given an expression in the form of : (* 3 (+ x y)), how can I evaluate the expression so as to put it in the form (+ (* 3 x) (* 3 y))? (note: in the general case, 3 is any constant, and "x" or "y" could be terms of single variables or other s-expressions (e.g. (+ 2 x)).
How do I write a lambda expression that will recursively evaluate the items (atoms?) in the original expression and determine whether they are a constant or a term? In the case of a term, it would then need to be evaluated again recursively to determine the type of each item in that term's list.
Again, the crux of the issue for me is the recursive "kernel" of the definition.
I would obviously need a base case that would determine once I have reached the last, single atom in the deepest part of the expression. Then recursively work "back up", building the expression in the proper form according to rules.
Coming from a Java / C++ background I am having great difficulty in understanding how to do this syntactically in Scheme.
Let's take a quick detour from the original problem to something slightly related. Say that you're given the following: you want to write an evaluator that takes "string-building" expressions like (* 3 "hello") and "evaluates" it to "hellohellohello". Other examples that we'd like to make work include things like
(+ "rock" (+ (* 5 "p") "aper")) ==> "rockpppppaper"
(* 3 (+ "scis" "sors")) ==> "scissorsscissorsscissors"
To tackle a problem like this, we need to specify exactly what the shape of the inputs are. Essentially, we want to describe a data-type. We'll say that our inputs are going to be "string-expressions". Let's call them str-exprs for short. Then let's define what it means to be a str-expr.
A str-expr is either:
<string>
(+ <str-expr> <str-expr>)
(* <number> <str-expr>)
By this notation, we're trying to say that str-exprs will all fit one of those three shapes.
Once we have a good idea of what the shape of the data is, we have a better guide to write functions that process str-exprs: they must case-analyze those three alternatives!
;; A str-expr is either:
;; a plain string, or
;; (+ str-expr str-expr), or
;; (* number str-expr)
;; We want to write a definition to "evaluate" such string-expressions.
;; evaluate: str-expr -> string
(define (evaluate expr)
(cond
[(string? expr)
...]
[(eq? (first expr) '+)
...]
[(eq? (first expr) '*)
...]))
where the '...'s are things that we'll be filling in.
Actually, we know how to fill in a little more about the '...': we know that in the second and third cases, the inner parts are themselves str-exprs. Those are prime spots where recurrence will probably happen: since our data is described in terms of itself, the programs that process them will also probably need to refer to themselves. In short, programs that process str-exprs will almost certainly follow this shape:
(define (evaluate expr)
(cond
[(string? expr)
... expr
...]
[(eq? (first expr) '+)
... (evaluate (second expr))
... (evaluate (third expr))
...]
[(eq? (first expr) '*)
... (second expr)
... (evaluate (third expr))
...]))
That's all without even doing any real work: we can figure this part out just purely because that's what the data's shape tells us. Filling in the remainder of the '...'s to make this all work out is actually not too bad, especially when we also consider the test cases we've cooked up. (Code)
It's this kind of standard data-analysis/case-analysis that's at the heart of your question, and it's one that's covered extensively by curricula such as HTDP. This is not Scheme or Racket specific: you'd do the same kind of data analysis in Java, and you see the same kind of approach in many other places. In Java, the low-mechanism used for the case analysis might be done differently, perhaps with dynamic dispatch, but the core ideas are all the same. You need to describe the data. Once you have a data definition, use it to help you sketch out what the code needs to look like to process that data. Use test cases to triangulate how to fill in the sketch.
You need to break down your problem. I would start by following the HtDP (www.htdp.org) approach. What are your inputs? Can you specify them precisely? In this case, those inputs are going to be self-referential.
Then, specify the output form. In fact, your text above is a little fuzzy on this: I think I know what your output form looks like, but I'm not entirely sure.
Next, write a set of tests. These should be based on the structure of your input terms; start with the simplest ones, and work upward from there.
Once you have a good set of tests, implementing the function should be pretty straightforward. I'd be glad to help if you get stuck!
I kinda understand how to convert elementary functions such as arithmetics to continuation-passing style in Scheme.
But what if the function involves recursion?
For example,
(define funname
(lambda (arg0 arg1)
(and (some procedure)
(funname (- arg0 1) arg1))))
Please give me advices.
Thank you in advance.
One place that has a good explanation on continuations and CPS is Krishnamurthi's PLAI book. The relevant part (VII) doesn't depend on other parts of the book so you can jump right in there. There is specifically an extended example of converting code to CPS manually, and tackling recursive functions (the first part of chapter 17).
In addition, I wrote an extended version of that text for my class, which has more examples and more details on the subject -- you might find that useful too. In addition to the PLAI text, I cover some common uses of continuations like implementing generators, the ambiguous operator and more. (But note that PLAI continues with a discussion of implementation strategies, which my text doesn't cover.)
(define (func x y k)
(some-procedure
(lambda (ret)
(if ret
(- x 1
(lambda (ret)
(func ret y k)))
(k #f))))
You are lacking a base case, which is why the only explicit call to the continuation is (k #f). If you have a base case, then you'd pass the base case return value to the continuation, also. For example:
(define (func x y k)
(zero? x
(lambda (ret)
(if ret
(k y)
(some-procedure
(lambda (ret)
(if ret
(- x 1
(lambda (ret)
(func ret y k)))
(k #f))))))))
This partly duplicates Chris Jester-Young's answer, but well, I hope I can explain it better :-).
In CPS, the difference you're seeking is between these two things (roughly):
You can invoke a procedure, and pass it the continuation you were passed. That's the equivalent of a direct-style optimized tail call.
Or, you can invoke a procedure, and pass in as its continuation a new procedure that does something with the "return value," passing in your original continuation. This is the equivalent of a direct-style stack call.
The latter is what the lambdas in Chris's example are doing. Basically, evaluating a lambda creates a closure—and these closures are used to do the same job that stack frames do in the execution of a a direct-style program. In place of the return address in a stack frame, the closure contains a binding for a continuation function, and the code for the closure invokes this.
Does anyone have a good guide as to how it works? Something with visual aids would be nice, every guide I've come across all seem to say the same thing I need a fresh take on it.
Here's the diagram that was left on our CS lab's whiteboard. So you're going to fetch some apples, and you grab a continuation before you begin. You wander through the forest, collecting apples, when at the end you apply your continuation on your apples. Suddenly, you find yourself where you were before you went into the forest, except with all of your apples.
(display
(call/cc (lambda (k)
(begin
(call-with-forest
(lambda (f)
(k (collect-apples f))))
(get-eaten-by-a-bear)))))
=> some apples (and you're not eaten by a bear)
I think a Bar Mitzvah and buried gold might have been involved.
Have a look at the continuation part of PLAI -- it's very "practical
oriented", and it uses a "black-hole" visualization for continuations that can help you
understand it.
There is no shortcut in learning call/cc. Read the chapters in The Scheme Programming Language or Teach Yourself Scheme in Fixnum Days.
I found that it helps to visualize the call stack. When evaluating an expression, keep track of the call stack at every step. (See for example http://4.flowsnake.org/archives/602) This may be non-intuitive at first, because in most languages the call stack is implicit; you don't get to manipulate it directly.
Now think of a continuation as a function that saves the call stack. When that function is called (with a value X), it restores the saved call stack, then passes X to it.
Never likes visual representation of call/cc as I can't reflect it back to the code (yes, poor imagination) ;)
Anyway, I think it is easier start not with call/cc but with call/ec (escape continuation) if you already familiar with exceptions in other languages.
Here is some code which should evaluate to value:
(lambda (x) (/ 1 x))
What if x will be equal '0'? In other languages we can throw exception, what about scheme?
We can throw it too!
(lambda (x) (call/ec (cont)
(if (= x 0) (cont "Oh noes!") (/ 1 x))))
call/ec (as well as call/cc) is works like "try" here. In imperative languages you can easily jump out of function simply returning value or throwing exception.
In functional you can't jump out, you should evaluate something. And call/* comes to rescue.
What it does it represent expression under "call/ec" as function (this named "cont" in my case) with one argument. When this function is called it replaces the WHOLE call/* to it's argument.
So, when (cont "Oh noes!") replaces (call/ec (cont) (if (= x 0) (cont "Oh noes!") (/ 1 x))) to "Oh noes!" string.
call/cc and call/ec are almost equals to each other except ec simplier to implement. It allows only jump up, whil cc may be jumped down from outside.
So I believe I understand continuations now, at least on some level, thanks to the community scheme wiki and Learn Scheme in Fixnum Days.
But I'd like more practice -- that is, more example code I can work through in my head (preferably contrived, so there's not extraneous stuff to distract from the concept).
Specifically, I'd like to work through more problems with continuations that resume and/or coroutines, as opposed to just using them to exit a loop or whatever (which is fairly straightforward).
Anyway, if you know of good tutorials besides the ones I linked above, or if you'd care to post something you've written that would be a good exercise, I'd be very appreciative!
Yeah, continuations can be pretty mind-bending. Here's a good puzzle I found a while back - try to figure out what's printed and why:
(define (mondo-bizarro)
(let ((k (call/cc (lambda (c) c)))) ; A
(write 1)
(call/cc (lambda (c) (k c))) ; B
(write 2)
(call/cc (lambda (c) (k c))) ; C
(write 3)))
(mondo-bizarro)
Explanation of how this works (contains spoilers!):
The first call/cc stores returns it's own continuation and stores it in k.
The number 1 is written to the screen.
The current continuation, which is to continue at point B, is returned to k, which returns to A
This time, k is now bound to the continuation we got at B
The number 1 is written again to the screen
The current continuation, which is to continue at point B, is returned to k, which is another (but different) continuation to another point B
Once we're back in the original continuation, it's important to note that here k is still bound to A
The number 2 is written to the screen
The current continuation, which is to continue at point C, is returned to k, which returns to A
This time, k is now bound to the continuation we got at C
The number 1 is written again to the screen
The current continuation, which is to continue at point B, is returned to k, which returns to C
The number 3 is written to the screen
And you're done
Therefore, the correct output is 11213. The most common sticking point I've put in bold text - it's important to note that when you use continuations to 'reset' the value of k that it doesn't affect the value of k back in the original continuation. Once you know that it becomes easier to understand.
Brown University's programming languages course has a problem set on continuations publicly available.
(define a 42)
(set! 'a 10)
(define a 42)
(define (symbol) 'a)
(set! (symbol) 10)
(define a (cons 1 2))
(set! (car a) 10)
I tried running them in DrScheme and they don't work. Why?
Think of set! is a special form like define which does not evaluate its first operand. You are telling the scheme interpreter to set that variable exactly how you write it. In your example, it will not evaluate the expression 'a to the word a. Instead, it will look for a variable binding named "'a" (or depending on your interpreter might just break before then since I think 'a is not a valid binding).
For the last set of expressions, if you want to set the car of a pair, use the function (set-car! pair val) which works just like any scheme function in that it evaluates all of its operands. It takes in two values, a pair and some scheme value, and mutates the pair so that the car is now pointing to the scheme value.
So for example.
>(define pair (cons 1 2))
>pair
(1 . 2)
>(set-car! pair 3)
(3 . 2)
Because the first argument of set! is a variable name, not an "lvalue", so to speak.
For the last case, use (set-car! a 10).
The issue is with (set! 'a 10), as you shouldn't be quoting the symbol a.
It sounds like you're trying to learn Scheme, and you don't know Lisp, yes? If so, I strongly recommend trying Clojure as an easier to learn Lisp. I failed to grasp the interaction between the reader, evaluation, symbols, special forms, macros, and so forth in both Common Lisp and Scheme because those things all seemed to interact in tangled ways, but I finally really understand them in Clojure. Even though it's new, I found Clojure documentation is actually clearer than anything I found for Scheme or CL. Start with the videos at http://clojure.blip.tv/ and then read the docs at clojure.org.