I just finished adding continuations to Meta/4 and I was wondering
what other people think of them.
I'm also wondering if anyone thought of a different way to include
continuations in a language like Joy or Meta/4?
Here's an example of how continuations work:
flag [ [ some-code 2 cont ] if more-code ] x
x is Meta/4's version of i (my initial reason for calling it x rather
than i was because Meta/4 was originally ment to be an extension of
Forth and I is already a Forth word - compatibility with Forth was
later dropped even though Meta/4 is still pretty much a mutated
version of it).
If flag is not 0 some-code gets executed followed by 2 cont. 2 cont
exits the 2 inner-most code stacks and continues the execution after
x so this particular structure resembles if/else construct. Here's a
possible implementation of if/else:
[ swap' swap [ [ #drop' 2 cont ] ifnot #drop ] x x ] " if/else" ;
This word expects flag code-stack1 code-stack2 on stack - if flag is
0 it executes code-stack2 otherwise it executes code-stack1.
swap' is the equivalent of swapd (similarly dup' is the equivalent of
dupd, x' is equivalent of swap dip, etc - also swap'' is the
equivalent of [swapd] dip and so on - swap'' and other '' words won't
be included in Meta/4 but I will probably define them in some
external library).
ifnot is the inverse of if - it executes the code stack only if flag
is 0.
Note that since Meta/4 is typeless stacks are discarded with #drop
rather than drop (drop would drop the stack from the top of the data
stack but it wouldn't free the memory resulting in a memory leak -
similarly duplicating a stack is done through #dup rather than dup).
The main problem with this version of continuations is that it
behaves differently with primitives than with user defined words. The
inconsistancy is best illustrated through an example:
[ [ some-code 2 cont dead-code ] x even-more-dead-code ] x
Here dead-code and even-more-dead-code never get executed.
Now if we define a word blah as [ x " blah" print ] " blah" ; and if
we substitute x in the above example with blah:
[ [ some-code 2 cont dead-code ] blah even-more-dead-code ] blah
then even-more-dead-code does get executed. The reason is that blah
itself is seen as a continuation and so 2 cont will skip dead-code
and " blah" print and resume at even-more-dead-code.
So in order to fix this 2 cont changes to 3 cont.
Note that that doesn't apply to the if/else construct above because
Meta/4, being properly tail recursive, performs tail call elimination
so the last x effectively branches into the appropriate code stack
rather than calling it (similarly if blah was defined as [ " blah"
print x ] there would be no problem either). This also wouldn't apply
if blah was defined as a macro that compiles x " blah" print into the
code itself.
I'm not particularly happy with this since it can get confusing. On
the other hand user defined control structures shouldn't be necessary
for the most part (recursion and continuations should be used as
alternatives).
As a possible solution to this problem I was thinking of adding a
continuation index stack - continuation index would be a value used
by cont to correct itself so it doesn't treat words like blah as
continuations. Normally the index would be 0 (for primitives like x,
if and ifnot) but words like blah would push 1 onto the continuation
index stack which would cause cont to skip 1 extra continuation).
However this solution is more complex and I don't really like it as
much as the current one as more problems are created then solved. I
don't however see a reason why this particular version of cont
couldn't be implemented on top of the current one if someone for
whatever reason felt it was necessary.
Ivan
--- In concatenative@y..., "e1_t" <e1_t@y...> wrote:
> I just finished adding continuations to Meta/4 and I was wonderingCorrection - due to tail call optimization this should be 1 cont
> what other people think of them.
>
> I'm also wondering if anyone thought of a different way to include
> continuations in a language like Joy or Meta/4?
>
> Here's an example of how continuations work:
>
> flag [ [ some-code 2 cont ] if more-code ] x
>
(which is the same as exit really).
Although cont could be defined as a macro that compiles cont nop
(where nop is a dummy word that does nothing) which would force cont
to not be tail call optimized.
Tail call optimization seems to only complicate things and maybe it
would be better if it was explicit rather than implicit in which case
it would be replaced by a word that transfers control to another word
rather than calling it. That could be achieved by using some sort of
an unconditional jump word that would look a lot like a goto. Any
thoughts on that? Suggestions?
Ivan