Musings first, possible answer at the end.
I think the code can be re-written like this:
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display #\@) cc) (call/cc (lambda (c) c)) )
( (lambda (cc) (display #\*) cc) (call/cc (lambda (c) c)) )
)
)
Or with some extra display statements to help see what is happening:
; create current continuation and tell us when you do
(define (ccc)
(display "call/cc=")
(call-with-current-continuation (lambda (c) (display c) (newline) c))
)
; call (yin yang)
(define (yy yin yang) (yin yang))
; run (call-yy) to set it off
(define (call-yy)
(yy
( (lambda (cc) (display "yin : ") (display #\@) (display cc) (newline) cc)
(ccc) )
( (lambda (cc) (display "yang : ") (display #\*) (display cc) (newline) cc)
(ccc) )
)
)
Or like this:
(define (ccc2) (call/cc (lambda (c) c)) )
(define (call-yy2)
(
( (lambda (cc) (display #\@) cc) (ccc2) )
( (lambda (cc) (display #\*) cc) (ccc2) )
)
)
Possible Answer
This may not be right, but I'll have a go.
I think the key point is that a 'called' continuation returns the stack to some previous state - as if nothing else had happened. Of course it doesn't know that we monitoring it by displaying @
and *
characters.
We initially define yin
to be a continuation A
that will do this:
1. restore the stack to some previous point
2. display @
3. assign a continuation to yin
4. compute a continuation X, display * and assign X to yang
5. evaluate yin with the continuation value of yang - (yin yang)
But if we call a yang
continuation, this happens:
1. restore the stack to some point where yin was defined
2. display *
3. assign a continuation to yang
4. evaluate yin with the continuation value of yang - (yin yang)
We start here.
First time through you get yin=A
and yang=B
as yin
and yang
are being initialised.
The output is @*
(Both A
and B
continuations are computed.)
Now (yin yang)
is evaluated as (A B)
for the first time.
We know what A
does. It does this:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B, we don't compute it.
4. compute another continuation B', display * and assign B' to yang
The output is now @*@*
5. evaluate yin (B) with the continuation value of yang (B')
Now (yin yang)
is evaluated as (B B')
.
We know what B
does. It does this:
1. restore the stack - back to the point where yin was already initialised.
2. display *
3. assign a continuation to yang - this time, it is B'
The output is now @*@**
4. evaluate yin with the continuation value of yang (B')
Since the stack was restored to the point where yin=A
, (yin yang)
is evaluated as (A B')
.
We know what A
does. It does this:
1. restores the stack - back to the point where yin and yang were being initialised.
2. display @
3. assign a continuation to yin - this time, it is B', we don't compute it.
4. compute another continuation B", display * and assign B" to yang
The output is now @*@**@*
5. evaluate yin (B') with the continuation value of yang (B")
We know what B'
does. It does this:
1. restore the stack - back to the point where yin=B.
2. display *
3. assign a continuation to yang - this time, it is B"
The output is now @*@**@**
4. evaluate yin (B) with the continuation value of yang (B")
Now (yin yang)
is evaluated as (B B")
.
We know what B
does. It does this:
1. restore the stack - back to the point where yin=A and yang were being initialised.
2. display *
3. assign a continuation to yang - this time, it is B'"
The output is now @*@**@***
4. evaluate yin with the continuation value of yang (B'")
Since the stack was restored to the point where yin=A
, (yin yang)
is evaluated as (A B'")
.
.......
I think we have a pattern now.
Each time we call (yin yang)
we loop through a stack of B
continuations until we get back to when yin=A
and we display @
. The we loop through the stack of B
continuations writing a *
each time.
(I'd be really happy if this is roughly right!)
Thanks for the question.