2

(Please see what is letrec first.)

The following code is obviously fine and self-explanatory:

;; -*- lexical-binding: t; -*-
(setq post-self-insert-hook `(,@(when (boundp 'post-self-insert-hook)
                                  post-self-insert-hook)
                              ,(letrec ((_ (lambda ()
                                             (message ":P")
                                             (remove-hook 'post-self-insert-hook _))))
                                 _)))

But if I use custom-set-variables,

;; -*- lexical-binding: t; -*-
(custom-set-variables
 '(post-self-insert-hook `(,@(when (boundp 'post-self-insert-hook)
                               post-self-insert-hook)
                           ;; Manual says `letrec' is
                           ;; only useful when lexical
                           ;; binding is in effect.
                           ;; But `custom-set-variables'
                           ;; evaluates these forms by
                           ;; calling `eval', which uses
                           ;; dynamic scope by default.
                           ,(let ((lexical-binding t))
                              (letrec ...)))))

Emacs will complain that Symbol’s value as variable is void: _.

Why the above code snippet doesn't work?


Irrelevant to This Question

For those who are not quite familiar with backquote:

(custom-set-variables
 '(post-self-insert-hook `(,@(when (boundp 'post-self-insert-hook)
                               post-self-insert-hook)
                           ,(let ((_ (lambda () "123")))
                              _))))
;; => nil

post-self-insert-hook
;; => (... (lambda nil "123"))

As you can see, the forms that are expected to be evaluated are indeed evaluated.

7
  • What exactly is the question? Seems like you started with a question, but you answered it in the question. Maybe you want to post that part as an answer instead, explaining a bit about it to help readers understand easier?
    – Drew
    Jun 24, 2023 at 13:52
  • @Drew: Alright, instead of explaining more, it might be more straightforward to just delete the sentence following 'why'. I will do that
    – shynur
    Jun 24, 2023 at 14:42
  • It is not really surprising, is it? In the first custom-set-variables, the expression is quoted, so nothing inside it gets evaluated. Am I missing something?
    – NickD
    Jun 24, 2023 at 18:57
  • @NickD: Perhaps you missed something. custom-set-variables uses eval to evaluate [backquote](,@(...) ,(letrec ...)) and then set the result as the value of post-self-insert-hook.
    – shynur
    Jun 24, 2023 at 19:24
  • 1
    @Drew: I think this question is about elisp now. Can I add this tag? :P
    – shynur
    Jun 24, 2023 at 20:04

1 Answer 1

3

Op missed the expected behavior of the code snippet, and does not provide the full code to reproduce the issue, which seriously obscurified the problem.

Short answer is, custom-set-variables used eval without setting the optional argument that enabled lexical scoping, so the arguments of custom-set-variables is eval in dynamic scope, thus letrec would not produce a closure as expected.

Switching between lexical/dynamic scoping should be done before parsing the program and cannot be changed by binding lexical-binding inside the program.

For example, in a new buffer using dynamic scope by default,

(let ((lexical-binding t))
  (lambda ()))
;; => (lambda nil)

then if you enable lexical binding and evaluate

(let ((lexical-binding nil))
  (lambda ()))
;; => (closure (t) nil)

It's obvious that (let ((lexical-binding nil/t)) ...) doesn't take effect.


Besides, just don't assume one who knows letrec can read your intension. As a punishment to Op, try explain how yin-yang puzzle works.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.