Emacs:在函数内使用add-hook(defun)

时间:2014-10-28 16:31:34

标签: emacs elisp hook

如果我这样做

(add-hook 'haskell-mode-hook
    (lambda ()
        (setq indent-tabs-mode t)
        (setq tab-width 4)
        (message "OK")))

在我的~/.emacs.d/init.el中,当我输入(lambda ...)时,haskell-mode会被执行。

但是,如果我使用这样的函数:

(defun my-add-hook (hook tmode twidth)
    (add-hook hook
        (lambda ()
            (setq indent-tabs-mode tmode)
            (setq tab-width twidth)
            (message "OK"))))

然后稍后在~/.emacs.d/init.el中调用它:

(my-add-hook 'haskell-mode-hook t 4)

然后没有任何反应(甚至没有显示“OK”消息)。是add-hook a defun内无法使用的特殊功能?我有每个项目 在单独的初始化文件中定义的设置,用于检测缓冲区名称 并将(lambda ()...)次调用添加到相关主要模式(在示例中) 上面,haskell-mode);我想通过使用瘦来减少代码冗长 像上面的my-add-hook这样的包装器,但我不知道为什么add-hook如此 困难的。

EDIT1:添加了澄清的代码。

EDIT2:我尝试使用my-add-hook时收到“文件模式规范错误:( void-variable tmode)”消息。

2 个答案:

答案 0 :(得分:6)

这是一个简单的修复,无需了解词法绑定:

(defun my-add-hook (hook tmode twidth)
  (add-hook hook
            `(lambda ()
               (setq indent-tabs-mode ,tmode)
               (setq tab-width ,twidth)
               (message "OK"))))

答案 1 :(得分:2)

您可以使用 lexical-let 重新绑定变量,然后 lambda 函数将保留其值:

(defun my-add-hook (hook tmode twidth)
  (lexical-let ((tmode tmode)
                (twidth twidth))
    (add-hook hook
        (lambda ()
            (setq indent-tabs-mode tmode)
            (setq tab-width twidth)
            (message "OK")))))

我不确定这是否是最惯用的Emacs Lisp代码,但它确实遵循Emacs Wiki文章DynamicBindingVsLexicalBinding中显示的相同模式,该文章将撰写定义为:

(defun compose (f g)
  (lexical-let ((f f)
                (g g))
    (lambda (x)
      (funcall f (funcall g x)))))