-
Notifications
You must be signed in to change notification settings - Fork 1
Interpreter Specs
During runtime, sclin represents each evaluation step as an ENV
, which
encapsulates the stack, scope, queue of commands, etc. into a thread-safe
container. You can consider ENV
as a snapshot of the program at a certain
point in evaluation. Most properties recorded by ENV
are immutable; for
instance, it uses a Scala Vector
to back the stack and an immutable Map for
the local scope. Exceptions to this convention exist: the global scope and line
cache are mutable, and both use thread-safe TrieMap
s.
ENV
's immutable properties make for a rather pleasant implementation of
sclin's underlying mechanics, primarily when working with pure code (i.e.
without side effects). For instance, when a sclin function finishes evaluating,
the interpreter rolls back to the outer ENV
, altering the stack if necessary.
The Q
builtin exemplifies this concept wonderfully:
1 2 4 8=$n ( $n 5* + \clr dip dup =$n ) Q
=> 1 2 4 44
Referring to the above code, let e1
be the ENV
before the Q
evaluation.
e1
looks something like:
== e1 ==
Stack: 1 2 4
Scope:
n = 8
Let e2
be the final ENV
inside the Q
evaluation, which ends up as:
== e2 ==
Stack: 44
Scope:
n = 44
Once the Q
finishes, the interpreter rolls back to e1
while taking e2
's
top of stack and pushing it to e1
's stack. As a result, the final ENV
looks like:
== e3 ==
Stack: 1 2 4 44
Scope:
n = 8
This is how sclin facilitates functional purity, even though transitioning from
ENV
to ENV
may give the illusion of the stack mutating.
Although sclin makes it easy to access side effects like the global scope and I/O, you should try to ensure that side effects occur in moderation under controlled circumstances. Doing so will make it easier to predict and reason through sclin code.
Made with ❤️ by Ben Pang (@molarmanful).