3 ms·
Thanks for this explanation. I find the topic interesting because I have been thinking about how to use a similar technique to enable interactive debugging of
by phaedrus 9y ago
Thanks for this explanation. I find the topic interesting because I have been thinking about how to use a similar technique to enable interactive debugging of meta-language expressions using the non-meta-level language's own debugger. Suppose you have a function that builds a (staged) expression object for later interpretation. If an exception occurs while the interpreter is executing it, or perhaps you merely want to see the code showing how/why the expression object was put together how it was, the information you get in the non-meta-level debugger will often be too low-level to be useful. And you can't even walk back up the stack to get it, because the function which built the expression has already returned, having completed it's job which was only to build the expression object, not to run it.
A solution is to make the function which builds the expression object be (1) a callback and (2) polymorphic in the staged sense as you described, where the exact same code 'x + x' can have different meanings in different stages. In the first pass, the objects the callback works with have operations which are defined to just build an expression object without evaluating it. In subsequent passes, the same code (or a differently parameterized specialization of the same generic code) is called again but this time it is manipulating objects bound to actual runtime state. The callback may or may not be actually doing any computation (perhaps the interpreter can or is evaluating the expressions itself, and the operations the callback is doing are all nops).
But just the act of re-threading execution back through the callback function gets the expression builder function's name back onto the stack for exception stack traces. It also provides a sort of "high level user interface for debugging": if the user puts a breakpoint in the callback code, examining the variables in it gives information about the runtime values passing through the interpreter. I.e. it would provide a way for the user to directly look for temporary variable "x" in the interpreter's current running state.
I guess that's in some sense the opposite of collapsing stages? In a way of thinking this is putting stages back in after they have been collapsed, for the purpose of tracing execution.