Search code examples
emacsscoping

elisp: two function with same argument name problem


I'm writing some util function in my emacs init.el. It seems that two function with same argument name cause some error.

If I change the argument name of one function, everything is ok. But since the argument is local to the function, they should not affect each other.

(defun for-loop (op list-var)
  (if (not (eq list-var nil))
    (progn
      (funcall op (car list-var))
      (for-loop op (cdr list-var)))))

(defun append-to-list (list-var element-list)
  (let ((op (lambda (item) (add-to-list list-var item))))
    (for-loop op element-list)))

(setq list-1 '(1 2 3))
(append-to-list 'list-1 '(4 5 6))

If I change the name 'list-var' in for-loop to some other name, 'anothoer-list-var' eg. everything goes OK, but as the code above, emacs print the stack:

Debugger entered--Lisp error: (wrong-type-argument symbolp (4 5 6))
  add-to-list((4 5 6) 4)
  (lambda (item) (add-to-list list-var item))(4)
  funcall((lambda (item) (add-to-list list-var item)) 4)
  (progn (funcall op (car list-var)) (for-loop op (cdr list-var)))
  (if (not (eq list-var nil)) (progn (funcall op (car list-var)) (for-loop op (cdr list-var))))
  for-loop((lambda (item) (add-to-list list-var item)) (4 5 6))
  (let ((op (function (lambda (item) (add-to-list list-var item))))) (for-loop op element-list))
  append-to-list(list-1 (4 5 6))

Solution

  • Welcome to the wonderful world of dynamic scoping!
    [ The currently active dynamic binding of list-var is the one done by for-loop rather than the one done by append-to-list, since it's closer in the stack, as you can see in the backtrace. ]

    First recommendation: don't use add-to-list on local variables, ever (better use push or cl-pusnew instead)!

    Second recommendation: add -*- lexical-binding:t -*- somewhere on the first line of your Elisp files, so that they use the saner lexical scoping by default.