Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic Binding in Common Lisp

This question is an extension of Common Lisp scoping (dynamic vs lexical)

I have read and (hopefully) understood the concepts of scoping and extent in Common Lisp (link: https://www.cs.cmu.edu/Groups/AI/html/cltl/clm/node43.html), but I am unable to get my head around the following three examples. All examples are run on a fresh lisp session in SBCL/Slime/Emacs.

Example 1: Prints 5 & 5

(defvar x 100)
(defun fun1 (x)
   (print x)
   (fun2))

(defun fun2 ()
   (print x))
     
(fun1 5)

Example 2: Prints 5 & 100

 (defun fun1 (x)
   (print x)
   (fun2))

 (defun fun2 ()
   (print x))
    
 (defvar x 100)
 
 (fun1 5)

Example 3: Prints 5 & 5 & 100

(defvar x 100)

(defun fun1 (x)
  (print x)
  (fun2))

(defun fun2 ()
  (print x))

(defvar x 100)
     
(fun1 5)

x

I understand why fun1 always prints 5 (due to lexical scope, but please correct if I'm wrong). What I don't understand is why fun2 prints 5 in Example 1, 100 in Example 2 and again 5 in Example 3?

  • Example 1: x, a variable with indefinite scope, is set to 5 in fun1 and accordingly fun2 access this value. Is this a correct interpretation?
  • Example 2: x is set to 100 by defvar, but why is not being re-set to 5 when fun1 is called? I thought the bindings took place when the functions are called or is it when they are defined? It appears that x is not yet bound when fun1 is defined, and therefore the binding of x in fun1 (which is lexically scoped) is not seen by the rest of the program and then the "global" binding takes place with the subsequent defvar. Is the behaviour x in the function call then due to lexical shadowing in fun1 but no dynamic shadowing for fun2? I.e. there are two different instances of x here since fun1 defined its x first and did not see a "global" x at the time.
  • Example 3: It appears here that since x is set globally first, both fun1 and fun2 are referencing the same instance of x and hence its value is updated during fun1 and applied also during fun2 (both are 5)? Furthermore I get 100 when I ask for the value of x at the end (why? when fun2 is returning 5?)

It has something to do with the following extract from Guy Steel's Common Lisp book, but I cannot get my head around it:

"Constructs that use lexical scope effectively generate a new name for each established entity on each execution. Therefore dynamic shadowing cannot occur (though lexical shadowing may). This is of particular importance when dynamic extent is involved."

Is the following statement always true (source: https://courses.engr.illinois.edu/cs421/sp2010/lectures/dynamicscope.pdf):

The binding rule in Lisp is this: a use of a name is bound to the most recent declaration of that name that is still live.

I'm starting to understand some of the parts, but cannot get a wholistic understanding of all three parts, so it would be very helpful if you could help.

like image 454
Ashok Khanna Avatar asked Jan 23 '26 06:01

Ashok Khanna


1 Answers

Some annotations to your code:

Example 1

(defvar x 100)    ; declares X to be special, globally and locally
                  ; also sets X to 100

(defun fun1 (x)   ; X is a dynamically bound variable
  (print x)       ; lookup of dynamic binding of X
  (fun2))        

(defun fun2 ()
  (print x))      ; lookup of dynamic binding of X
     
(fun1 5)

Example 2

 (defun fun1 (x)  ; X is a lexical local variable
   (print x)      ; lexical reference to X
   (fun2))

 (defun fun2 ()
   (print x))     ; X is undeclared/undefined
                  ; the exact behaviour is undefined in Common Lisp
                  ; many implementations assume dynamic lookup of X
                  ; most compilers will show a warning
                  ; CMUCL also by default declared X globally to be special
                  ; -> don't use this in your code
    
 (defvar x 100)   ; declares X to be special, globally and locally 
                  ; also sets X to 100     
 (fun1 5)

Example 3

(defvar x 100)    ; declares X to be special, globally and locally
                  ; also sets X to 100

(defun fun1 (x)   ; X is a dynamically bound variable
  (print x)       ; lookup of dynamic binding of X
  (fun2))

(defun fun2 ()
  (print x))      ; lookup of dynamic binding of X

(defvar x 100)    ; does nothing
                  ;  -> X is already declared special
                  ;  -> X already has a value
                  ;     see also: DEFPARAMETER
     
(fun1 5)

x                 ; lookup of global (or thread local) value of X
like image 75
Rainer Joswig Avatar answered Jan 25 '26 08:01

Rainer Joswig



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!