Skip to content

Interpreter Specs

molarmanful edited this page Nov 4, 2023 · 4 revisions

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 TrieMaps.

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.