Normative Rules: Variable Scope (Dynamic, Not Lexical)
← Prev | ↑ Chapter | Next → | Index | Symbols
Normative Rules: Variable Scope (Dynamic, Not Lexical)
Statement
AutoLISP variables are dynamically scoped. A binding established at function call entry — through a positional parameter, through a (/ locals) declaration, or through any subsequent setq on those names — is visible to every function called transitively from the body. The binding is restored to its prior value when the function returns. There is no lexical capture: a lambda does not close over the names referenced by its body. When the lambda is later called, every free reference is resolved against the active dynamic-frame chain, then the active namespace.
Concrete Consequences
(defun make-adder (/ x) (setq x 10) (lambda (y) (+ x y)))— calling(make-adder)then later applying the returned lambda outsidemake-adder's extent fails:xhas no binding any more, so the body's reference toxresolves tonil(see "Unbound-Variable Reference" below) and the(+ ... y)call surfaces bad argument type <NIL>; expected <NUMBER> at [+].- A counter built with
(defun with-counter (/ k) (setq k 0) (lambda () (setq k (+ k 1)) k))does not accumulate state across calls; each call from outsidewith-counterseeskunbound (and thusnil). - A lambda referencing a global (top-level) symbol picks up that global's current value at every call, not the value at lambda creation. Mutating the global between two calls of the same lambda changes what subsequent calls return.
Vendor Evidence
BricsCAD V26 / macOS direct probe (section C):
- Captured-local closure call: error bad argument type <NIL>; expected <NUMBER> at [+] — the local was popped on return; the lambda saw
nil. - Top-level closure:
(c2)first returns7(set inside maker2), then42after a top-level(setq x 42). - Local counter: every call from outside the maker fails with the same NIL-at-[+] error — no accumulation.
Strict / Lax Policy
Dynamic scoping is strict across all supported dialects. No host product is known to expose lexical-capture semantics in its AutoLISP layer; any such extension would require a separate dialect knob.
Cross-references
- Compare CLHS 3.1.1 — Common Lisp is lexical by default; dynamic scope requires
defvar/(declare (special …)). - Compare R7RS 5.2 — Scheme is unconditionally lexical.
- Special Form Entry: LAMBDA, FUNCTION (this chapter) — produce function values; do not capture.
- Single-Cell Symbol Binding (chapter 7) — the dynamic frame chain consults the same per-symbol binding cell.
Source Notes
- [About Local and Global Variables (AutoLISP)](https://help.autodesk.com/cloudhelp/2023/ENU/AutoCAD-AutoLISP/files/GUID-A8045F62-1CE3-4022-ACC1-CC0E2C1E158D.htm) — describes shadowing pragmatically.
- Phase-6 BricsCAD V26 / macOS semantics probe section C.