Home > Software engineering >  Common Lisp: How do I set a variable in my parent's lexical scope?
Common Lisp: How do I set a variable in my parent's lexical scope?

Time:09-27

I want to define a function (not a macro) that can set a variable in the scope its called.

I have tried:

(defun var-set (var-str val)
  (let ((var-interned
          (intern (string-upcase var-str))))
    (set var-interned val)
    ))

(let ((year "1400"))
  (var-set "year" 1388)
  (labeled identity year))

Which doesn't work because of the scoping rules. Any "hacks" to accomplish this?

In python, I can use

previous_frame = sys._getframe(1)
previous_frame_locals = previous_frame.f_locals
previous_frame_locals['my-var'] = some_value

Any equivalent API for lisp?

CodePudding user response:

You cannot do that because after compilation the variable might not even exist in any meaningful sense. E.g., try to figure out by looking at the output of (disassemble (lambda (x) ( x 4))) where you would write the new values of x.

You have to tell both the caller and the callee (at compile time!) that the variable is special:

(defun set-x (v)
  (declare (special x))
  (setq x v))
(defun test-set (a)
  (let ((x a))
    (declare (special x))
    (set-x 10)
    x))
(test-set 3)
==> 10

See Dynamic and Lexical variables in Common Lisp for further details on lexical vs dynamic bindings.

CodePudding user response:

(defvar *lexical-variables* '())

(defun get-var (name)
  (let ((var (cdr (assoc name *lexical-variables*))))
    (unless var (error "No lexical variable named ~S" name))
    var))

(defun deref (var)
  (funcall (if (symbolp var)
               (or (cdr (assoc var *lexical-variables*))
                   (error "No lexical variable named ~S" var))
               var)))

(defun (setf deref) (new-value var)
  (funcall (if (symbolp var)
               (or (cdr (assoc var *lexical-variables*))
                   (error "No lexical variable named ~S" var))
               var)
           new-value))

(defmacro with-named-lexical-variable ((&rest vars) &body body)
  (let ((vvar (gensym))
        (vnew-value (gensym))
        (vsetp (gensym)))
    `(let ((*lexical-variables* (list* ,@(mapcar (lambda (var)
                                                   `(cons ',var
                                                          (lambda (&optional (,vnew-value nil ,vsetp))
                                                            (if ,vsetp
                                                                (setf ,var ,vnew-value)
                                                                ,var))))
                                                 vars)
                                       *lexical-variables*)))
       ,@body)))


(defun var-set (var-str val)
  (let ((var-interned (intern (string-upcase var-str))))
    (setf (deref var-interned) val)))


(let ((x 1)
      (y 2))
  (with-named-lexical-variable (x y)
    (var-set "x" 3)
    (setf (deref 'y) 4)
    (mapcar (function deref) '(x y))))
;; -> (3 4)


(let ((year "1400"))
  (with-named-lexical-variable (year)
    (var-set "year" 1388))
  year)
;; --> 1388

CodePudding user response:

What are you trying to achieve?

What about a closure? Write the defun inside the let, and create an accessor and a setter function if needed.

(let ((year "1400"))
  (defun current-year ()
    year)

  (defun set-year (val)
    (setf year val)))

and

CL-USER> (current-year)
"1400"
CL-USER> (set-year 1200)
1200
CL-USER> (current-year)
1200
  • Related