Originally, following Push3 and Clojush's model, instructions put their "results" onto the expected stacks directly.
However, this leads to several problems, especially in situations where the interpreter (temporarily) doesn't recognize the type of a result, or where the result is a :ref. Further, several core instructions useful for recursion and iteration produce "continuations" that are pushed onto :exec to be processed in subsequent steps.
The architectural problem entailed by the original approach is the pressure to "check the type" of things pushed directly onto stacks by instructions. It is more satisfying—and maintainable—if all instructions use the same "router" to send their instructions to to the correct place, rather than independently shoving things around wherever the author decides they should go.
Recently, I decided to put "all" results of instructions back onto the :exec stack, and let the router put them where they should be. This also has problems, though, since several instructions intentionally subvert ""exec routing", like :code-quote. Worse, the combinator instructions can be thought of as "working on" an entire stack of stuff; for example, :scalar-yank arguably "consumes" one :scalar as its first argument, and then the entire :scalar stack, and produces as its result a new:scalar stack.
A further complication and point of confusion comes from the role of :binding stacks. These are not "types", but then again neither are core Push items "typed". So the metaphor should be simpler to understand if we simply consider "stack" to mean "specific address", not something associated with a particular type as such.
So it's time to put things back something like they used to be, but more mindfully.
The Router functionality of the Interpreter record will be upgraded to be a self-contained functional object in its own right, not just a static vector. It will include information about all "addressable stacks", not just the explicit stacks but also the :binding stacks, including the :ARGS and other "special" bindings; the router can be queried at runtime to determine the "address" to which an item would be sent, or all "addresses" to which it might be sent. This latter surfaces the "type system" and permits an item to be (1) a vector of numbers and (2) a vector at the same time.
"type" will be abstracted away to mean "address", and expanded to include :binding addresses in addition to traditional :type and :module stacks. This means all "addresses" are in fact reference able as ":refitems, and so raw code can contain:codeor:scalarsaddresses as such. The behavior of some:ref` instructions will have to be adjusted to suit, to avoid (literal) type problems.
Instructions can obtain arguments from addresses only by using the DSL consume-top-of addressname or copy-top-of addressname; items can be pushed to an address by calling push-onto addressname (which will send the item immediately without checking routing), or route-item, which will (immediately!) invoke the router to send the item to the current route destination.
Instructions can read whole stacks only by using consume-entire addressname or copy-entire addressname; an entire address can be replaced only as a whole using replace-entire addressname
(not quite done here)
Problems to address: Introspection can produce "type addresses" in code, or "type signatures" of code blocks. Care will need to be taken to avoid accidentally permitting inappropriate items to end up on "typed" stacks.
Originally, following Push3 and Clojush's model, instructions put their "results" onto the expected stacks directly.
However, this leads to several problems, especially in situations where the interpreter (temporarily) doesn't recognize the type of a result, or where the result is a
:ref
. Further, several core instructions useful for recursion and iteration produce "continuations" that are pushed onto:exec
to be processed in subsequent steps.The architectural problem entailed by the original approach is the pressure to "check the type" of things pushed directly onto stacks by instructions. It is more satisfying—and maintainable—if all instructions use the same "router" to send their instructions to to the correct place, rather than independently shoving things around wherever the author decides they should go.
Recently, I decided to put "all" results of instructions back onto the
:exec
stack, and let the router put them where they should be. This also has problems, though, since several instructions intentionally subvert ""exec
routing", like:code-quote
. Worse, the combinator instructions can be thought of as "working on" an entire stack of stuff; for example,:scalar-yank
arguably "consumes" one:scalar
as its first argument, and then the entire:scalar
stack, and produces as its result a new:scalar
stack.A further complication and point of confusion comes from the role of
:binding
stacks. These are not "types", but then again neither are core Push items "typed". So the metaphor should be simpler to understand if we simply consider "stack" to mean "specific address", not something associated with a particular type as such.So it's time to put things back something like they used to be, but more mindfully.
Router
functionality of theInterpreter
record will be upgraded to be a self-contained functional object in its own right, not just a static vector. It will include information about all "addressable stacks", not just the explicit stacks but also the:binding
stacks, including the:ARGS
and other "special" bindings; the router can be queried at runtime to determine the "address" to which an item would be sent, or all "addresses" to which it might be sent. This latter surfaces the "type system" and permits an item to be (1) a vector of numbers and (2) a vector at the same time.:binding
addresses in addition to traditional:type
and:module
stacks. This means all "addresses" are in fact reference able as ":refitems, and so raw code can contain
:codeor
:scalarsaddresses as such. The behavior of some
:ref` instructions will have to be adjusted to suit, to avoid (literal) type problems.consume-top-of addressname
orcopy-top-of addressname
; items can be pushed to an address by callingpush-onto addressname
(which will send the item immediately without checking routing), orroute-item
, which will (immediately!) invoke the router to send the item to the current route destination.consume-entire addressname
orcopy-entire addressname
; an entire address can be replaced only as a whole usingreplace-entire addressname
(not quite done here)
Problems to address: Introspection can produce "type addresses" in code, or "type signatures" of code blocks. Care will need to be taken to avoid accidentally permitting inappropriate items to end up on "typed" stacks.