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.

Drew
  • 75,699
  • 9
  • 109
  • 225
shynur
  • 4,065
  • 1
  • 3
  • 23
  • 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 '23 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 '23 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 '23 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 '23 at 19:24
  • 1
    @Drew: I think this question is about `elisp` now. Can I add this tag? :P – shynur Jun 24 '23 at 20:04
  • Not in my opinion. When there are more-specific tags that cover it, don't use `elisp`. E.g., tag `elisp-macros` for Qs about Elisp macros. (I added tag `eval` here.) Tag `elisp` (IMO) is essentially for Qs about differences between Elisp and other Lisps. – Drew Jun 25 '23 at 01:20
  • Another tag that might be useful here is `lexical-binding`. Pick the (max 5) tags you think are most appropriate/helpful. – Drew Jun 25 '23 at 01:24

1 Answers1

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.

shynur
  • 4,065
  • 1
  • 3
  • 23
LdBeth
  • 171
  • 2