Search code examples
emacslispsymbols

Using elisp symbol to implement call-by-reference, but can not get value from `symbol-value`


Emacs-lisp is default using call-by-value, but I'm trying use its symbol mechanism to simulate call-by-reference.

For example,

(setq lexical-binding nil)

(defun cbr (x)
  (message "cbr (symbol-name x) %s" (symbol-name x))
  (message "cbr (symbol-value x) %s" (symbol-value x))
  (set x 2))

(let ((a 1))
  (cbr 'a)
  a)
;; cbr (symbol-name x) a
;; cbr (symbol-value x) 1
;; 2

It works well, because the result of let expression is 2, so it is indeed the call-by-reference behavior.

However, if I change the name from a to x:

(let ((x 1))
  (cbr 'x)
  x)
;; cbr (symbol-name x) x
;; cbr (symbol-value x) x
;; 1

Now it doesn't work as expected anymore.

Why?

Notice that it even can not get the correct symbol-name in cbr.


Solution

  • I think I have known what happen.

    The second program returns 1, because the symbol x is captured by cbr's param x. When the body of cbr is evaluated, there are two bindings in the environment: one is the let binding x = 1, the other is x = x which is created by cbr's application. The symbol x in the (set x 2) uses the later one.

    A workaround of this question is:

    (let ((gen-x (gensym)))
      (set gen-x 1)
      (cbr gen-x)
      (symbol-value gen-x))
    ;; cbr (symbol-name x) g36
    ;; cbr (symbol-value x) 1
    ;; 2