3

I came across something that I can not understand.

#lang scheme

(define cc #f)

(define (val!)
  (call/cc
   (lambda (k)
     (set! cc k)
     0)))

(* 10 (val!))

(cc 100)

So far so good; the continuation of (* 10 []) is stored in cc and if we call (cc 100) we see 1000 in the REPL as expected.

But the next thing I tried was to define a variable to be the result of running the continuation:

(define x (cc 20))

I see 200 as a result in the REPL, but x does not get defined.

Does the continuation stored in cc include its returning so that the call to define never returns and instead the evaluation is the result of (* 10 val)? What is going on?

Alex Knauth
  • 8,133
  • 2
  • 16
  • 31
  • If you want continuations to return values, you should use composable continuations, by setting up a prompt and using `call-with-composable-continuation`. – Alex Knauth Sep 30 '17 at 04:27

2 Answers2

3

What's going on?

There are two types of continuations.

A continuation produced by call/cc never returns a value to its caller. See Will Ness's answer for more on that.

But the continuations produced by call-with-composable-continuation are composable continuations, which do return values.

The solution

If you want a continuation to return a value to its caller, you should use a composable continuation, by setting up a prompt and using call-with-composable-continuation.

You can define a kind of prompt:

(define my-prompt
  (make-continuation-prompt-tag 'my-prompt))

And use the prompt in call-with-composable-continuation to specify that you only want to capture the continuation starting from the prompt.

(define cc #f)

(define (val!)
  (call-with-composable-continuation
   (lambda (k)
     (set! cc k)
     0)
   my-prompt))

Then you just have to put the prompt wherever you want the continuation to start before you call val! to save it.

;; the prompt specifies that it's `(* 10 [])`, and not something larger
(call-with-continuation-prompt
 (λ () (* 10 (val!)))
 my-prompt)

Then, since this continuation has a clear "end" defined by the prompt, it can return a value when it reaches that end.

(define x (cc 20))
; defines x as 200

See also: What exactly is a "continuation prompt?"

Alex Knauth
  • 8,133
  • 2
  • 16
  • 31
3

It returns nothing, because it does not return. (cc 20), just as (cc 100), does not return a value to its caller. cc is not a function, it is a continuation - it remembers where to return / "feed" its value, by itself.

(define x (cc 20))

means, approximately, in pseudocode,

(let ([val (cc 20)])
  (primitive-define-top-level-var! "x" val))

but (cc 20) bypasses the setting of val and using it to define x, and returns directly to the top level, as was done by the original captured continuation.

Does the continuation stored in cc include its returning ["destination" -- wn] so that the call to define never returns and instead the evaluation is the result of (* 10 val)?

Yes.

What is going on?

Exactly that.


edit:

Loading the following in DrRacket,

#lang scheme

(define cc #f)
(define (val!)
  (call/cc
   (lambda (k)
     (set! cc k)
     0)))
(* 10 (val!))
(cc 100)
(display "Good!\n")
(define x (cc 20))
(display "Shucks!\n")

I even get an error message explaining what is going on:

Language: scheme, with debugging; memory limit: 128 MB.
0
1000
Good!
200
define-values: skipped variable definition;
  cannot continue without defining variable
    variable: x
    in module: 'anonymous-module

>

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • And of course if you would wrap the code in `let` so you have no continuation barrier between the expressions you'll see that it becomes an infinite loop doing `(* 10 20)` – Sylwester Sep 30 '17 at 23:40
  • @Sylwester I couldn't understand your comment. Could you show the full code that you mention? Mine was just a pseudocode. – Will Ness Oct 01 '17 at 07:35
  • Just wrap the whole top level expressions in a `(let () ...)` and you never get "Good!\n" output since the previous line jumps back to `(* 10 100)` every time. The behaviour with `define` is not specified in R5RS, which is what PLTs `#lang scheme` is supposed to follow. – Sylwester Oct 01 '17 at 14:36
  • thanks for the clarification. Of course, that "makes sense", because `let` has an implicit `begin`. – Will Ness Oct 01 '17 at 16:17
  • 1
    @Sylwester `#lang scheme` is badly named; it does not follow any Scheme standard. It is a holdover from the era of PLT Scheme, and it is essentially `#lang racket`. Only `#lang r5rs` follows R5RS. – Alexis King Oct 02 '17 at 06:07
  • @AlexisKing I couldn't even find a documentation for it. – Will Ness Oct 02 '17 at 08:52
  • @AlexisKing It's the same as `#lang racket` before they made the pairs immutable. It conforms to R5RS with lots of incompatible extra bindings, just like any R5RS would do. Will: The split was announced with [this blogpost](https://racket-lang.org/new-name.html) and [here is the documentation](http://docs.racket-lang.org/scheme/index.html?q=scheme%2Fbase) – Sylwester Oct 04 '17 at 17:32
  • @Sylwester That’s not true; pairs in `#lang scheme` have been immutable ever since they made the pairs immutable. There is no reason to use `#lang scheme`. It is a legacy language for backwards compatibility only. – Alexis King Oct 04 '17 at 17:34
  • @AlexisKing That's unfortunate for the "for backwards compatibility" part. I guess that's what broke Grahams Arc a while back. – Sylwester Oct 04 '17 at 17:44
  • @Sylwester The purpose was to make old code written in `#lang scheme` work with new code written in `#lang racket`, so the process of porting to `#lang racket` could be more incremental instead of needing to be all-at-once. There’s still a fair number of modules in Racket itself written in `#lang scheme`, I believe. People usually convert them whenever they need to change them for some reason or another. – Alexis King Oct 04 '17 at 17:47