by Maarten Keijzer (maarten)
This message in response to a request from Lee to try to explain what the ENV concept was and how it relates to function isolation in Push.
The concept is really simply and relies upon a single observation: given a push program and its current state (i.e., the states of the stacks), can we construct a program that, when run on an ’empty’ interpreter (an environment, ENV), will reproduce the exact state of the running program. This is possible.
Suppose you execute a program ( CODE.QUOTE (a b) 1 2 + 3 *), and everything before the ‘*’ sign has executed. If we want to describe what is going on we can say something like:
The INT stack contains a 3 followed by a 3
The Code stack contains the program (a b)
The EXEC stack contains the single instruction ‘*’
We could however also say that the ENV is described by the program
( 3 3 CODE.QUOTE (a b) * )
[Updated 19th March, the code above had a plus where it should be multiplication]
Which effectively, when run in a clean environment, will reconstruct the original situation. It’s not that hard to create a program like this from within any given interpreter (and it’s a great way to achieve persistence, serialization, etc.).
I’ve always taken this method of creating a program out of an environment to be the ideal return value of a function call. This has lead me to define a function call in Push3 as follows.
- ENV.DO will take the top of the code stack and start running this piece of code in a new interpreter (puts it on the EXEC stack there)
- every execution slice that is allocated to this env will go to this new interpreter instead, until…
- When the execution stack of the new interpreter is empty, i.e., it is done, create a program of the ENV, and treat that as a return value by either putting it on the CODE stack or directly on the EXEC stack (I usually pick the latter)