Scheme (and thus GoLisp) is lexically scoped. This is implemented by the creation of a lexical environment (aka symbol table) for each lexical scope:
- function/lambda/macro invocations, which holds parameters and any local definitions
let
structures, which hold the let
bindings
do
structures, which hold the do
bindings
Functions and lambdas capture a reference to the environment in which they were defined, so they always have access to its bindings (that’s a closure, btw).
Each environment has a connection to its containing environment, and can override/hide bindings in outer scopes. When a symbol is evaluated, the most local environment is searched first. If a binding for the system isn’t found there, the containing environment is searched. This continues until a binding for the sybol is found or we go all the way to the global environment and still can’t find a binding.
Section 3.2 of [1] does a great job of explaining environments in Scheme, which is the basis for environments in GoLisp.
In Scheme, some environments are more important than others, mainly as they tend to be larger, long lived, and serve as the root of many other environments as a program runs. These are known as top level environments. Specifially, these are the global environment (the only environment that is contained by nothing), and any environments directly below it in the environment tree. The REPL runs in one such environment, which effectively sandboxes it, protecting the bindings in the global environment from corruption.
In standard Scheme, environments are first class objects. That is, they can be passed around, returned, manipulated, etc. I’ve recently added this ability to GoLisp, along with the majority of the environment manipulation functions from Scheme. These are described below.
Read full post