Forgive my verbosity. I'm not exactly sure how to describe my situation.
Given the following...
(defun three () 3)
(defun four () 4)
(defun makeplusser (x)
(list 'defun (make-symbol (format "%s+" x)) '(y)
(list '+ (list x) 'y)))
In my scratch buffer, I type the following then hit C-j (eval-print-last-sexp)...
(makeplusser 'three)
Which gets me this...
(defun three+ (y) (+ (three) y))
... Which I highlight, hit C-j, and am able to use...
(three+ 4) ; => 7
When I do this...
(eval (makeplusser 'four)) ; => four+
It dumps the function name "four+" to the buffer which leads me to believe it's been properly defun'd. When I try to use it...
(four+ 3)
I get the following error message:
Debugger entered--Lisp error: (void-function four+) (four+ 3)
eval((four+ 3))
Questions are:
1- Your problem is not caused by differences between eval-print-last-sexp
and eval
.
For example, if you eval
the following snippet (for example using C-x C-e), everything works
(setq exp (list 'defun 'four+ '(y) (list '+ '(four) 'y)))
(eval exp)
(four+ 4)
whereas evaluating in the same way the following snippet doesn't work as expected:
(setq exp (makeplusser 'four))
(eval exp)
(four+ 4)
even though the value of exp
is identical in both cases
2- The actual problem you are facing is related to the way you create the symbol for your function: make-symbol
creates an uninterned symbol, which will not be visible from outside the function call. You should create an interned symbol instead, like this :
(defun makeplusser (x)
(list 'defun (intern (format "%s+" x)) '(y)
(list '+ (list x) 'y)))
(eval (makeplusser 'four))
3- For such things, you should consider writing a macro instead of evaluating the result of a function:
(defmacro makeplusser (x)
(let ((name (intern (format "%s+" x)))
(argsym (make-symbol "arg")))
`(defun ,name (,argsym)
(+ (,x) ,argsym))))
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
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))))
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".
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With