0

As described in the title I try to solve the following problem:

(setq s ())

(defun reassign (val)
  (setq val 10))

(reassign s)

(print s)
-> Output: s = nil

I want reassign to destructively assign s = 10. Is there an easy way to handle this, without using the variable name s within the function & only changing the function? (may Macros be included?) I would be grateful for any advice! :)

coredump
  • 37,664
  • 5
  • 43
  • 77
Kisslax
  • 3
  • 2

2 Answers2

2
(defun reassign (val)
  (setq val 10))

(reassign s)

When you call (reassign s), the value of s is passed down to reassign, where it is locally bound to val. That would be nil in your case, assuming you already declared s with DEFVAR before setting it to () (see setq and defvar in Lisp).

Inside reassign, the call to SETQ changes the local binding.

Global binding

Each symbol can hold a global value. If you want to change the value cell of a symbol, use the SYMBOL-VALUE accessor:

(setf (symbol-value 's) 10)

Notice how s is quoted. You are not changing the symbol currently bound to s (which would be nil, a constant variable), but the s symbol itself. (SETF SYMBOL) is like calling SET directly.

Lexical and dynamic binding

If however, you want to modify any kind of place, and in particular lexical and dynamic variable bindings, you need to define a macro:

(defmacro reassign (place)
  `(setf ,place 10))

SETF expands into the code necessary to update a place. You could also give (gethash key table) instead of just an unquoted variable, which would then update the content of a table.

In the case of local variables, the code that calls reassign will eventually expands into a call to the special operator SETQ, which knows how to change lexical bindings (it also handles SYMBOL-MACROLET bindings).

coredump
  • 37,664
  • 5
  • 43
  • 77
1

Yes, you guessed correctly. you need a macro.

(defmacro reassign (var &optional (val 10)) `(setq ,var ,val))

(defvar s ())   ;; @Svante remarked that setq can be dangerous to create a variable
s ;; NIL
(reassign s)
s ;; 10

;; the `&optional (val 10)` makes the value optional and 10 to the 
;; default value of val if not given.

In your example

(defun reassign (val) (setq val 10))

val is the first argument of the special form of setq: the val in the setq doesn't become s although you gave it reassign for val. It stays val so that you assign 10 in the functional scope to val. You can make this visible by:

(setq s ())

(defun reassign (val)
  (print "You gave for `val`:")
  (print val) 
  (setq val 10)
  (print "The form assigned 10 literally to val")
  (print "Proof: Print out val in this scope:")
  (print val))

(reassign s)

;; 
;; "You gave for `val`:" 
;; NIL 
;; "The form assigned 10 literally to val" 
;; "Proof: Print out val in this scope:" 
;; 10 
;; 10

(print val)

*** - SYSTEM::READ-EVAL-PRINT: variable VAL has no value
The following restarts are available:
USE-VALUE      :R1      Input a value to be used instead of VAL.
STORE-VALUE    :R2      Input a new value for VAL.
ABORT          :R3      Abort main loop
;; since val has not been initiated before

In addition, the setq of the function is executed within the functional scope. That is why outside the function val keeps the old value.

(defvar val ()) ;; NIL
(print val)   ;; NIL

(defun reassign (val)
  (print "You gave for `val`:")
  (print val) 
  (setq val 10)
  (print "The form assigned 10 literally to val")
  (print "Proof: Print out val in this scope:")
  (print val))

(reassign 'whatever) ;; inside function val becomes 10
;;
;;"You gave for `val`:" 
;;WHATEVER 
;;"The form assigned 10 literally to val" 
;;"Proof: Print out val in this scope:" 
;;10 ;; val becomes indeed 10 until leaving function 
;;10

;; but retains old value after leaving the function
(print val)
;;
;; NIL
;; NIL

The macro in contrast replaces the macro call (reassign s) by the form (setq s 10). So when executed, the reassignment takes place within the environment where we defined (defvar s ()). Therefore after the reassignment, s keeps the newly assigned value, since it was reassigned in the working environment.

Gwang-Jin Kim
  • 9,303
  • 17
  • 30