-
Notifications
You must be signed in to change notification settings - Fork 5
old‐sh‐sh Scopes
A namespace is a named global scope, all namespaces are layered on top of the root namespace (which contains all the core symbols). The current namespace is the current global scope. A namespace can export symbols via the ns-export form and import another namespaces symbols via ns-import (no need to import the root namespace). Any binding created with the def
form or one of it's derivatives (defn
, defmacro
) will be interned in the current namespace (note this is determined lexically so using def in a lambda will but the symbol in the namespace where the lambda is defined not where it is called).
See https://sstanfield.github.io/slsh/#Namespace%20forms-body for the namespace form docs.
Symbols in a namespace can be accessed from another namespace via a "long name", for instance the sym
symbol in the example
namespace can be accessed as example::sym
from any namespace even if not imported.
NOTE: The symbols in a namespace can only be written to (or added) from within the namespace.
NOTE: Shell environment variables are not effected by namespace changes, just lisp symbols.
Creating a lambda also creates a new lexical scope. Lambda are created via the fn
primitive which is the basis of several macros (defn
, varfn
, let
, lex
, loop
, etc). Anything that creates a new lexical scope ultimately does so via fn
. The lambda will capture any variables in containing lexical scopes that it references (when the fn
is evaluated). This allows lambdas to assess data that may not be visible to code that can access it's symbol allowing simple classes or other forms of data hiding/sharing to be implemented. The let
macro is a wrapper around fn
, creating something similar to
((fn (p1 p2 ... pn) (body)) v1 v2 ... vn)
If you are already in a lambda then using var
to add a symbol to it's scope is probably better then a macro like let
. In sl-sh the var
form will add a symbol to the current lexical scope.
Note: Using def
in a lambda will create a global binding in the current namespace- use var
for local variable bindings.
Example of lambdas and lexical scopes:
; Define a symbol at the enclosing scope that will be assigned a lambda below.
; Even though it is created in an anonymous lambda it will keep captured bindings alive.
(fn (x y z)
(var a 1)
(defn some-fn (b c) (
; The body of some-fn will be able to access x y z and a if it references them (captures them) and always b c since they are local.
))
Sl-sh also supports dynamic scoping via the dyn
builtin. By creating a dynamic scope the dynamic binding(s) will take precedent over any global bindings while they are in scope. This applies to anything called indirectly as well (i.e. the *stdout* variable could be overwritten for all code called while dynamic binding is in force. Calls to dyn
can be nested and binding will be replaced as inner dyn
s exit. Dynamic scope is intended for special cases and is not ergonomic to use for multiple bindings at once (would require nested dyn
forms).
Example:
(defn test-dyn-fn () (print \"Print dyn out\"))
(dyn *stdout* (open \"/tmp/sl-sh.dyn.test\" :create :truncate) (do (test-dyn-fn)))
(test::assert-equal \"Print dyn out\" (read-line (open \"/tmp/sl-sh.dyn.test\" :read)))