Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Emacs: use add-hook inside function (defun)

Tags:

emacs

hook

elisp

If I do

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

in my ~/.emacs.d/init.el, then the (lambda ...) does get executed when I enter haskell-mode.

However, if I use a function like this:

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

and then call it later in ~/.emacs.d/init.el like so:

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

Then nothing happens (even the "OK" message isn't displayed). Is add-hook a special function that cannot be used from within a defun? I have per-project settings defined in a separate initialization file that detects the buffer name and adds (lambda ()...) calls to the pertinent major mode (in the example above, haskell-mode); I want to reduce the code verbosity by using a thin wrapper like my-add-hook above, but I cannot tell why add-hook is being so difficult.

EDIT1: Added code for clarification.

EDIT2: I do get a "File mode specification error: (void-variable tmode)" message when I try to use my-add-hook.

like image 703
Linus Arver Avatar asked Feb 11 '23 16:02

Linus Arver


2 Answers

Here's a simple fix without needing to know about lexical binding:

(defun my-add-hook (hook tmode twidth)
  (add-hook hook
            `(lambda ()
               (setq indent-tabs-mode ,tmode)
               (setq tab-width ,twidth)
               (message "OK"))))
like image 165
abo-abo Avatar answered Feb 23 '23 23:02

abo-abo


You can use lexical-let to rebind the variables, then the lambda function will preserve their values:

(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")))))

I'm not sure whether this is the most idiomatic Emacs Lisp code, but it does follow the same pattern shown in the Emacs Wiki article DynamicBindingVsLexicalBinding, which defines compose as:

(defun compose (f g)
  (lexical-let ((f f)
                (g g))
    (lambda (x)
      (funcall f (funcall g x)))))
like image 30
Joshua Taylor Avatar answered Feb 23 '23 23:02

Joshua Taylor