Search code examples
emacskey-bindings

Why does key binding cause Emacs to execute my function on startup?


I have a function in my Emacs init.el file that lets me rebuild and byte-compile it from a literate source file. It consists of a lambda function wrapped by defun and works exactly as I expect. So far, so good.

(defun tangle-init-and-reload ()
  "Tangle the code blocks in init.org, byte-compile, and reload."
  (lambda ()
    (interactive)
    ;; Don't run hooks when tangling.
    (let ((prog-mode-hook nil))
      (org-babel-tangle-file (concat user-emacs-directory "init.org"))
      (byte-compile-file (concat user-emacs-directory "init.el"))
      (load-file user-init-file))))

When I read about functions in Elisp, it appears to me that I should be able to simply use defun to define a named function and skip the lambda, so I removed the lambda and otherwise left the function intact, like so:

(defun tangle-init-and-reload ()
  "Tangle the code blocks in init.org, byte-compile, and reload."
  (interactive)
  ;; Don't run hooks when tangling.
  (let ((prog-mode-hook nil))
    (org-babel-tangle-file (concat user-emacs-directory "init.org"))
    (byte-compile-file (concat user-emacs-directory "init.el"))
    (load-file user-init-file)))

Written this way, the function also works as expected -- as long as I call it with M-x tangle-init-and-reload RET. If I assign it a key binding, it executes on startup with one of two different side effects: With some key bindings, it attempts to overwrite init.elc while Emacs still has it open, and with others it successfully overwrites init.elc, but then re-executes on reload, causing an infinite recursion.

I'm perfectly happy to stick with the lambda version, which has no issues with key binding, but I would like to understand what magic lambda is performing and/or what it is about key binding that causes the second version to execute at startup. Can anybody explain?

For whatever it's worth, my key bindings are in a custom minor mode like so:

(defvar custom-map (make-keymap)
  "Custom key bindings.")
(define-key custom-map (kbd "C-c C-i") (tangle-init-and-reload))
(define-minor-mode custom-bindings-mode
  "Activates custom key bindings."
  t nil custom-map)

Solution

  • When you define the key binding, you associate a key to a value, which in your case is:

    (tangle-init-and-reload)
    

    This is an expression that is evaluated normally, ie. you call the function when you associate the binding.

    In the previous version, evaluating the same function returned a closure, you had one level of indirection, so you established a binding from a key to the function returned by the call to tangle-init-and-reload.

    You can simply give the name of the function associated with the binding, by quoting it:

    (define-key custom-map (kbd "C-c C-i") 'tangle-init-and-reload)