评估 let 内部的文件



如果my-variable全局未定义,并且my-file.el使用它,

;; -*-  lexical-binding: t; -*-
(let((my-variable 1))
(load "/path/to/my-file.el"))

报告错误:

Debugger entered--Lisp error: (void-variable my-variable)

另一方面

;; -*-  lexical-binding: t; -*-
(defvar my-variable 11)
(let((my-variable 1))
(load "/path/to/my-file.el"))

使用本地定义的值 1 工作。 为什么我必须使用defvar

PS:my-file.el可以是单行:

(1+ my-variable)

let所在的文件可能正在使用lexical-binding,这意味着:

(let ((my-variable 1))
(load "/path/to/my-file.el"))

在使用它的my-variable的词汇范围内没有任何内容。

如果你对文件进行字节编译,它会告诉你:

警告:未使用的词法变量"my-variable">

添加defvar会将其标记为动态作用域变量,在这种情况下,加载my-file.el时计算的代码也将看到 let 绑定值。

请注意,没有值的(defvar my-variable)也足以做到这一点,如果这不是应该定义变量的地方,您应该使用这种值。

请参阅使用词法绑定:

在 Lisp 交互和 IELM 模式下也启用了词法绑定,用于暂存ielm缓冲区,以及通过 M-: (eval-expression) 计算表达式以及处理 Emacs 的 --eval 命令行选项(参见 GNU Emacs 手册中的操作参数)和 emacsclient(参见 GNU Emacs 手册中的 emacsclient 选项)。

因此,如果在*scratch*缓冲区中评估let,它将使用词法绑定,这意味着变量的值仅绑定到词法上下文,即在加载其他文件时不会保留它。

另一种可能性是从打开词法绑定的.el文件中运行它。如果将其关闭,它应该按预期方式工作。使用缓冲区局部变量lexical-binding启用或禁用它。

[不是答案,但我不想随意编辑 OP 的问题(而且评论太长了)。我只是提供了我如何重现问题的描述,因为@choroba似乎得到了不同的结果]

我在/tmp/my-file.el有这个:

(if (= my-variable 1) (message "Yes") (message "No"))

我用emacs -Q启动 emacs,并在*scratch*缓冲区中评估以下内容(潜入版本信息):

(emacs-version '(4))
GNU Emacs 28.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.24.25, cairo version 1.16.0)
of 2021-03-07nil
(let ((my-variable 1))
(message (format "%d" my-variable))
(load "/tmp/foo.el"))

我得到以下回溯:

Debugger entered--Lisp error: (void-variable my-variable)
(= my-variable 1)
(if (= my-variable 1) (message "Yes") (message "No"))
eval-buffer(#<buffer  *load*> nil "/tmp/foo.el" nil t)  ; Reading at buffer position 54
load-with-code-conversion("/tmp/foo.el" "/tmp/foo.el" nil nil)
load("/tmp/foo.el")
(let ((my-variable 1)) (message (format "%d" my-variable)) (load "/tmp/foo.el"))
(progn (let ((my-variable 1)) (message (format "%d" my-variable)) (load "/tmp/foo.el")))
eval((progn (let ((my-variable 1)) (message (format "%d" my-variable)) (load "/tmp/foo.el"))) t)
elisp--eval-last-sexp(t)
eval-last-sexp(t)
eval-print-last-sexp(nil)
funcall-interactively(eval-print-last-sexp nil)
call-interactively(eval-print-last-sexp nil nil)
command-execute(eval-print-last-sexp)

我得到的结果与我的发行版(Fedora 33)中打包的emacs完全相同:GNU Emacs 27.1 (build 1, x86_64-redhat-linux-gnu, GTK+ Version 3.24.22, cairo version 1.16.0) of 2020-08-20

最新更新