Disclaimer: I don't know Emacs Lisp, but I know Lisp.
What I believe you are running into is read/print confusion with uninterned symbols. That is to say, I suspect that (make-symbol ...)
in Emacs Lisp, just like the same-named function in other dialects such as Common Lisp, creates a new symbol object which has nothing to do with a symbol of the same name that is scanned from a printed notation (from a file, terminal, string, edit buffer, ...).
Your code works when you grab the printed output of your code-generating function (a.k.a S-expression), because the printed notation of the symbol four
is read-back into Lisp and interned, causing that notation to become the same object as the name of the function four
.
But (make-symbol "four")
is a different symbol object. When you use eval
on the code which comes out of your function, you are using the data structure directly and so your mistake is not covered up by reducing the code to text and reading it back. The symbol does not get converted to the token four
and back to the object four
via interning. eval
will see your original symbol that came from make-symbol
: the same machine pointer to the same piece of memory.
(In Common Lisp, the "uninterned symbol" coming from (make-symbol "four")
is normally printed with a hash-dot notation, like #:four
so you can spot them. (Actually #:
means symbol with no home package, not uninterned, but that's very obscure Common Lisp subtlety.))
Anyway, look for a function called intern
. (intern "four")
will look up the existing symbol with that name and return it, rather than make a new one.
;; two symbol interns for same name result in the same object
;; we are comparing the same pointer to itself
(eq (intern "foo") (intern "foo")) -> t
;; two symbol constructions result in two different object
;; two different pointers to separately allocated objects
(eq (make-symbol "foo") (make-symbol "foo")) -> nil
Also:
You really must learn backquote if you want to write code-generating code. Otherwise you're doing it in a 1960's way rather than the modern 1970's way:
;; don't let your friends do this:
(defun makeplusser (x)
(list 'defun (make-symbol (format "%s+" x)) '(y)
(list '+ (list x) 'y)))
;; teach them this:
(defun makeplusser (x)
`(defun ,(intern (format "%s+" x)) (y)
(+ (,x) y)))
;; even more clearly, perhaps
(defun makeplusser (func-to-call)
(let ((func-name (format "%s+" x)))
`(defun ,func-name (arg)
(+ (,func-to-call) arg))))
When to use make-symbol
Use make-symbol
when you need to create a guaranteed unique symbol (not the same object as any other symbol) even if it happens to have the same name as other symbols. Other Lisp dialects also have a function called gensym
which is like make-symbol
but it also appends an incrementing numeric stamp to the name to make it easier to distinguish these "gensyms" when multiple ones occur in the same context. gensym
is much more commonly used than make-symbol
in Lisps that have it. Unique symbols are useful in code generation (macros) for generating unique labels for things that must be absolutely invisible to the surrounding code, such as temporary local variables within the inserted block of code. Your function's argument should be a unique symbol:
;; even more clearly, perhaps
(defun makeplusser (func-to-call)
(let ((func-name (format "%s+" x))
(arg-sym (make-symbol "arg"))
`(defun ,func-name (,arg)
(+ (,func-to-call) ,arg))))
The reason is: Emacs Lisp is dynamically scoped. If we call the argument y
, there is a risk that the user function which is called, like (four)
might contain a reference to y
, where the programmer's intent is to reach his or her own variable y
. But your generated function accidentally captures the reference!
By using a gensym for the argument, we avoid that problem; there is no chance that the user's code can refer to the argument: we have achieved "hygiene" or "transparency".