1

I'm playing around with implementing looping using call/cc and wrote a map function like this:

(define (map1 f l)
  ((lambda (state)
     (let ((cc (car state))
           (l (cadr state))
           (m (caddr state)))
       (if (null? l) (reverse m)
        (cc (list cc (cdr l) (cons (f (car l)) m))))))
   (call-with-current-continuation
    (lambda (kk) (kk (list kk l '()))))))

So far so good, (map1 (lambda (n) (+ n 2)) '(3 5 11 17)) returns (5 7 13 19). Next I wanted to get rid of the list encapsulation in the state argument with multiple values in the following function:

(define (map2 f l)
  (call-with-values
   (lambda () (call-with-current-continuation
               (lambda (kk) (kk (values kk l '())))))
   (lambda (cc l m)
     (if (null? l) (reverse m)
         (cc (values cc (cdr l) (cons (f (car l)) m)))))))

Racket, Chez, Chicken, gauche and scm all give errors that 1 value was expected but 3 received. Interestingly, gambit gives the expected result (the one I expected, that is). What am I missing in my understanding of call-with-values and/or call/cc? Or can someone re-write my map2 so it works?

4
  • 1
    Try (kk kk l '()); no values Commented Aug 24 at 7:37
  • @Shawn you do need values in the initial "generator", but not the call to the continuation itself. (yeah the documentation is Racket's, but it works in Racket's r5rs mode, I checked). Commented Aug 27 at 20:53
  • @WillNess Not if you have a continuation. Both places can call the continuation as if it was values. So in the generator both (values kk l '()) and (kk kk l '()) will work, while in the receiver only calling the continuation will work. Commented Sep 3 at 0:18
  • @Sylwester I was referring to the specific code as it appears in the question. I'm sure the community would welcome and appreciate it if you posted a Q&A that illustrated the point you're making here with some specific code as an example. Posting a self-answered question is welcome on SO. Commented Sep 4 at 14:01

1 Answer 1

1

You were pretty close. Here it is:

(define (map2 f l)
  (call-with-values
   (lambda () (call-with-current-continuation
               (lambda (cc) (values cc l '()))))
   (lambda (cc l m)
     (if (null? l) (reverse m)
         (cc cc (cdr l) (cons (f (car l)) m))))))

;;; > (map2 (lambda (n) (+ n 2)) '(3 5 11 17))
;;; '(5 7 13 19)

You do need values in the initial "generator", but not the call to the continuation itself (the linked documentation is Racket's, but it works in Racket's r5rs mode, I checked).

And you do not need values in the "receiver" lambda's tail call. The three values get fed to the receiver lambda itself, which expects the three arguments.

Having foo = (lambda (a b c) ...), calling (call-with-values (lambda () (values x y z)) foo) is the same as calling (foo x y z):

> (call-with-values 
     (lambda () (values 1 2 3))
     (lambda (a b c) (list c b a)))
'(3 2 1)
> ((lambda (a b c) (list c b a)) 1 2 3)
'(3 2 1)
> 

call-with-values just supplies the values as arguments in the function call. The continuation, then, is just calling the lambda function with the supplied values. Which is what we're doing in the tail call of that same lambda, thus, looping.

Just as the documentation is saying,

    (call-with-values generator receiver) → any

 generator : (-> any)
 receiver : procedure?

Calls generator, and passes the values that generator produces as arguments to receiver. Thus, call-with-values creates a continuation that accepts any number of values that receiver can accept.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.