Emacs的init.el中触发Error running timer: (void-variable message)报错是什么原因?
报错原因
该错误是Emacs Lisp默认使用动态作用域导致的变量捕获失效问题:
- 你写的匿名lambda内部引用了外层函数的
message参数,动态作用域规则下,变量会在代码执行时的上下文查找,而非代码定义时的上下文。 run-at-time设置的计时器触发lambda执行时,外层的cypher/cowsayx-sclock函数已经执行完毕退出,message变量在当前执行上下文中已被销毁,因此抛出未定义变量的错误。
修复方案
下面提供3种常用的修复方案,任选一种即可:
方案1:利用run-at-time原生传参特性(兼容性最好)
run-at-time支持在执行函数后附加任意参数,执行时会自动将参数传入目标函数,不需要依赖变量捕获:
(defun cypher/cowsayx-sclock (in-minutes message) (interactive "nSet the time from now - min.: \nsWhat: ") (run-at-time (* in-minutes 60) nil (lambda (msg) (message "%S" msg) (shell-command (format "xcowsay %s" (shell-quote-argument msg)))) message))
方案2:开启当前配置文件的词法作用域
在你的init.el文件最顶部添加如下局部变量声明,开启当前文件的词法作用域,lambda会自动捕获定义时的外部变量,原有代码无需修改即可正常运行:
;;; -*- lexical-binding: t; -*-
方案3:手动用lexical-let捕获变量
如果你不想修改全局的作用域配置,也可以用lexical-let手动捕获需要的变量:
(defun cypher/cowsayx-sclock (in-minutes message) (interactive "nSet the time from now - min.: \nsWhat: ") (lexical-let ((captured-msg message)) (run-at-time (* in-minutes 60) nil (lambda () (message "%S" captured-msg) (shell-command (format "xcowsay %s" (shell-quote-argument captured-msg)))))))
内容的提问来源于stack exchange,提问作者Achylles
相关产品推荐
相关产品推荐

