3

Honnestly, I'm not sure I fully understand what it means for a binding to be "dynamic" versus "lexical". But I understand that when I use defvar or defparameterto define a binding, 1. it declares a global variable 2. the binding is declared "special", so that it can be shadowed by a new local binding, e.g.

(defvar *x* 3)
(defun f () *x*)
(f) ;=>3
(let ((*x* 2)) (f));=>2

Now, my question is, would it be possible to have a local binding(i.e. a binding that doesn't pollute the global environment) that has the same property(i.e. that can be shadowed by "outer"/"newer" bindings)?

Example:

(special-binding ((x 1)) (defun f () x))
(f);=>1
x;=>error, no binding in the global environment
(let ((x 2)) (f));=>2

I tried using (special x) declarations in let block, or (locally (declare (special x)) ...), but it doesn't seem to create closures(asking for the value of the variable from a function defined that way triggers an "Unbound-Variable" error).

royhowie
  • 11,075
  • 14
  • 50
  • 67
Charles Langlois
  • 4,198
  • 4
  • 16
  • 25

2 Answers2

5

You can't capture dynamic bindings in a closure, only lexical bindings.

You need to declare the variable special in the function, so it will use the dynamic binding:

(defun f () 
  (declare (special x))
  x)

Then you need to bind the variable dynamically around the call, with:

(let ((x 1))
  (declare (special x))
  (f))
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • Actually, the [`special` declaration outside the `let` doesn't work](http://www.lispworks.com/documentation/HyperSpec/Body/d_specia.htm). Quoting the relevant part: "A special declaration does not affect inner bindings of a var; the inner bindings implicitly shadow a special declaration and must be explicitly re-declared to be special." You must have tested that code with `x` previously proclaimed `special` globally, e.g. through `defvar` or `defparameter`. – acelent Jun 09 '15 at 11:33
  • Thanks, I was just going from memory, I didn't test it. – Barmar Jun 09 '15 at 14:45
3

First, a dynamic variable only takes its value from dynamic bindings, not lexical:

(defun f ()
  (declare (special x))
  x)

(let ((x 1))
  ;; without this declaration, we would get an unbound variable error
  (declare (special x))
  (f))
;; => 1

You can achieve a default value for a local dynamic binding using progv:

(defun b ()
  (progv (if (boundp 'x) () '(x))
      (if (boundp 'x) () '(1))
    (locally (declare (special x))
      x)))

(let ((x 2))
  (declare (special x))
  (b))
;; -> 2

(b)
;; -> 1
m-n
  • 1,476
  • 1
  • 9
  • 9