Emacs lisp - eval unexpectedly different from eval-print-last-sexp

Forgive my verbosity. I am not quite 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 buffer, from scratch, I type the following, and then press Cj (eval-print-last-sexp) ...

 (makeplusser 'three) 

Which gets me from this ...

 (defun three+ (y) (+ (three) y)) 

... which I highlight, press Cj and can use ...

 (three+ 4) ; => 7 

When I do this ...

 (eval (makeplusser 'four)) ; => four+ 

It dumps the name of the four + function into the buffer, which makes me believe that it was correctly defun'd. When I try to use it ...

 (four+ 3) 

The following error message appears:

Debugger introduced - Lisp error: (function void + +) (four + 3)
eval ((four + 3))

Questions:

  • Why does (eval-print-last-sexp) behave differently than using (eval)?
  • How can I get my plusser functions without having to evaluate the generated defun in my buffer? I would like to be able to just (mapcar # 'eval (mapcar #' makeplusser '(three four))) or something, but this does not work the way I expect.
+4
source share
2 answers

1 - Your problem is not caused by the differences between eval-print-last-sexp and eval .

For example, if you eval following snippet (e.g. using Cx Ce ), everything works

 (setq exp (list 'defun 'four+ '(y) (list '+ '(four) 'y))) (eval exp) (four+ 4) 

whereas with the same calculation, the following fragment does not work properly:

 (setq exp (makeplusser 'four)) (eval exp) (four+ 4) 

although exp identical in both cases

2 - The real problem that you are facing is related to how you create a symbol for your function: make-symbol creates an uninterrupted symbol that will not be displayed outside the function call. Instead, you should create an intern character, for example:

 (defun makeplusser (x) (list 'defun (intern (format "%s+" x)) '(y) (list '+ (list x) 'y))) (eval (makeplusser 'four)) 

3 - For such things, you should consider creating 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)))) 
+8
source

Disclaimer: I do not know Emacs Lisp, but I know Lisp.

I believe that you are working, this is a confusion of reading / printing with non-integer characters. That is, I suspect that (make-symbol ...) in Emacs Lisp, like a similar function in other dialects such as Common Lisp, creates a new symbol object that has nothing to do with the symbol of the same name that is being scanned from printed notation (from file, terminal, line, buffer editing, ...).

Your code works when you grab the printed output of your code generation function (also called an S-expression), because the printed notation of the four character is read in Lisp and interned, which causes the notation to become the same object as the name of the four function .

But (make-symbol "four") is another symbol object. When you use eval for code that exits your function, you use the data structure directly, and therefore, your error is not covered by reducing the code to text and reading it. The character is not converted to the four token and back to the four object through internment. eval will see your original character that came from make-symbol : the same machine pointer to the same piece of memory.

(In general, Lisp, the "uninterterned" character coming from (make-symbol "four") is usually printed using a hash point, such as #:four so you can find them. (Actually, #: means a symbol without a home package, not uninterned, but this is a very obscure Common Lisp subtlety.))

In any case, find a function called intern . (intern "four") will search for an existing character with this name and return it, rather than creating 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 need to learn backquote if you want to write code that generates code. Otherwise, you do this in 1960, and not in modern 1970:

 ;; 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 has the same name as other symbols. Other Lisp dialects also have a function called gensym , which is similar to make-symbol , but also adds an incremental digital mark to the name so that it is easier to distinguish between these "gensyms" when several occur in the same context. gensym much more commonly used than make-symbol in Lisps that use it. Unique characters are useful when generating code (macros) to create unique shortcuts for things that should be completely invisible to the surrounding code, such as temporary local variables in the built-in code block. Your function argument must be a unique character:

 ;; 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 this: Emacs Lisp has a dynamic scope. If we call the argument y , there is a risk that the function called by the user, for example (four) , may contain a reference to y , where the programmer's intention is to achieve his own variable y . But your generated function accidentally captures the link!

Using gensym for an argument, we avoid this problem; there is no chance that the user code may refer to an argument: we have achieved "hygiene" or "transparency."

+6
source

Source: https://habr.com/ru/post/1402664/


All Articles