This page of the Factor manual talks about these types of stacks stored in continuations:
What precisely do these stacks hold? The three most confusing for me are the callstack, retainstack and namestack.
I'm certainly no factor guru, but since their names seem to imply their use:
datastack: used for the usual pushing and popping of values. 3 4 +
uses the datastack to push a '3', then push a '4'. Running '+' pops 2 values from the datastack and pushes the answer, 5, back to the datastack. Running an interactive session of factor (at least on Linux) prints the contents of this stack after every interaction:
$> 1
--- Data stack:
1
$> 2
--- Data stack:
1
2
$> +
--- Data stack:
3
$> .
3
$>
callstack: used to store which words are running and their individual progress while their component words execute. Imagine you have defined a better version of sum: : sum' ( seq-of-int -- summmation ) 0 [ + ] reduce 20 + ;
(it's better because you get an extra 20 for free!). Wanting to reuse code, you've leveraged the reduce
word that comes with standard factor. While the runtime is executing sum'
it calls the implementation of reduce
. But, since we still need to add the extra 20, someone has to record where to startup again once reduce
returns. These notes are stored on the callstack, most likely with some ancillary data during debug runs to help debuggers understand what is going on.
retainstack: used for retaining values on a sort of ancillary datastack. In Forth, one can abuse the return stack (Forth's analog to the callstack) to act as a retainstack. One of the problems with this approach is that returning from your word without cleaning up your dirty hack will have you jumping to incorrect locations and wreaking general havoc. Forth's runtime will see your values, expecting them to be the nice notes it makes when calling a word and gets confused. By using a separate stack for return addresses, Factor can avoid this.
namestack: used to hold the data necessary to implement dynamic variables. By using a stack, you can hide old names with new ones while executing a subroutine, then pop the binding and the old names are restored.
catchstack: used to support exception handling. By using a stack, subroutines can register their own specialty handlers for exceptions and shadow default behaviour. Then, once the word returns, the old handlers can be restored easily by popping from the stack.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With