6

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.

Brian
  • 61
  • 1

5 Answers5

11

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.

call/cc

(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.

erjiang
  • 44,417
  • 10
  • 64
  • 100
5

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.

Eli Barzilay
  • 29,301
  • 3
  • 67
  • 110
0

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.

paul
  • 1,100
  • 1
  • 8
  • 14
0

There is no shortcut in learning call/cc. Read the chapters in The Scheme Programming Language or Teach Yourself Scheme in Fixnum Days.

knivil
  • 916
  • 5
  • 10
0

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.

Hans Nowak
  • 7,600
  • 1
  • 18
  • 18