I'm experimenting with writing generators and coroutines and thought I'd
post the findings here in case others have questions.
For a coroutine code I wanted to create a continuation that when called
returns back to the current caller, not the original caller when the
continuation was captured.
Here's some code:
: test
[ "test1 escaping continuation is " print
dup print
test2
] callcc1
"end-of-test" print ;
: test2 ( escape -- )
"test2 will call escape continuation" print
dup print
[
"test2 continuation to re-enter is " print
dup print
swap call
] callcc1
nip
"Stack after calling of test2s re-enter continuation is" print
.s
call ;
: test3 ( cont -- )
"calling test2s re-enter continuation" print
dup print
[
"test3 escaping continuation which test should call is" print dup print
swap call
] callcc0
drop
"test3-end" print ;
The intent is to provide a continuation in 'test2' that can be called to
re-enter test2 but exit to the caller of the continuation, not to the
original caller of 'test2'. So calling 'test' will return to
"end-of-test" and calling test3 on the continuation returned by 'test'
will return to 'test3-end'.
So I'll call 'test', which will place the continuation on the stack. Then
call 'test3' which calls that continuation and passes it the continuation
to call to exit back. I'm printing out the values of the continuations so
I can keep track of what is happening.
343] clear
344] .s
345] test
test1 escaping continuation is
[ #<factor.FactorArrayStack@120dbf3> #<factor.FactorCallStack@4845aa>
#<factor.FactorArrayStack@d5c0f9> [ unit ] continue ]
test2 will call escape continuation
[ #<factor.FactorArrayStack@120dbf3> #<factor.FactorCallStack@4845aa>
#<factor.FactorArrayStack@d5c0f9> [ unit ] continue ]
test2 continuation to re-enter is
[ #<factor.FactorArrayStack@1701bdc> #<factor.FactorCallStack@1353249>
#<factor.FactorArrayStack@1786286> [ unit ] continue ]
end-of-test
346] .s
[ #<factor.FactorArrayStack@1701bdc> #<factor.FactorCallStack@1353249>
#<factor.FactorArrayStack@1786286> [ unit ] continue ]
And this is the continuation that I will want to call when running
'test3'. Note that it is the one from the 'test2 continuation to re-enter
is' line which is correct.
346] dup
347] test3
calling test2s re-enter continuation
[ #<factor.FactorArrayStack@1701bdc> #<factor.FactorCallStack@1353249>
#<factor.FactorArrayStack@1786286> [ unit ] continue ]
test3 escaping continuatoin which test should call is
[ #<factor.FactorArrayStack@8c5ea2> #<factor.FactorCallStack@198defc>
#<factor.FactorArrayStack@1579a30> [ unit ] continue ]
Stack after calling of test2s re-enter continuation is
[ #<factor.FactorArrayStack@120dbf3> #<factor.FactorCallStack@4845aa>
#<factor.FactorArrayStack@d5c0f9> [ unit ] continue ]
[ #<factor.FactorArrayStack@8c5ea2> #<factor.FactorCallStack@198defc>
#<factor.FactorArrayStack@1579a30> [ unit ] continue ]
test3-end
This works fine. Because of the 'dup' I can recall it by running 'test3'
and it works again, and again. Nice!
A couple of points that tripped me up when first experimenting:
1) I can't just 'call' the continuation captured by 'test2'. If I do this
I'll end up returning back to 'test' with its stack restored rather than
where I want to be in the current call stack. This is why 'test3' is used
to capture a new escaping continuation and pass it to the continuation
I'm calling.
2) I got confused by what state the stack should be after calling a
continuation. I had to remember to drop items off the stack that I no
longer needed after the 'callcc0' or 'callcc1' call as the stack is
restored to the point after callcc0 or callcc1 has popped the code to be
executed off the stack. So:
: x ( a b c -- ) [ >r drop drop drop r> call ] callcc0 .s ;
1 2 3 x ( 1 2 3 -- )
: y ( a b c -- ) [ drop drop drop drop ] callcc1 .s ;
1 2 3 y ( -- )
This is why there is the 'nip' in 'test2' and the 'drop' in test3 after
the callcc.
Am I understanding how continuations work in Factor correctly?
Chris.
--
Chris Double
chris.double@...