0

I'm trying to write a helper macro to conveniently define keybindings in my configuration file.

The idea is to replace the following configuration entry:

(use-package org-roam
  :config
  (define-key evil-normal-state-map (kbd "SPC n g") #'org-roam-graph)
  (define-key evil-insert-state-map (kbd "C-c n g") #'org-roam-graph)
  (define-key evil-visual-state-map (kbd "C-c n g") #'org-roam-graph))

With that one:

(use-package org-roam
  :config
  (define-evil-key "n g" #'org-roam-graph))

Here's how I'm trying to write such a macro:

(defmacro define-evil-key (k fn)
  (define-key evil-normal-state-map (kbd (concat "SPC " k)) fn)
  (define-key evil-insert-state-map (kbd (concat "C-c " k)) fn)
  (define-key evil-visual-state-map (kbd (concat "C-c " k)) fn))

But when I press the defined keybinding SPC n g, I get the following error: Wrong type argument: commandp, #'org-roam-graph.

I'm not really sure, what is going wrong. It seems to me, that the macros should be expanded on the sequence of define-key calls, but it doesn't work that way.

Could someone please explain how to properly define this macros and how to debug similar errors in the future?

2
  • 1
    Maybe you should define a function (using defun) rather than a macro? Dec 7, 2021 at 8:01
  • What @Lindydancer said.
    – Drew
    Dec 7, 2021 at 16:16

1 Answer 1

2

A macro has to return code. The return value of your macro is whatever the last form evaluated to:

(defmacro define-evil-key (k fn)
  (define-key evil-normal-state-map (kbd (concat "SPC " k)) fn)
  (define-key evil-insert-state-map (kbd (concat "C-c " k)) fn)
  (define-key evil-visual-state-map (kbd (concat "C-c " k)) fn))

(pp-macroexpand-expression '(define-evil-key "x" #'foo))
=> #'foo

Being what the last define-key form had evaluated to.

The specific error "Wrong type argument: commandp, #'org-roam-graph." is related to the fact that macro arguments are not evaluated -- this prevents the define-key calls from binding the intended command.

You wanted something like this:

(defmacro define-evil-key2 (k fn)
  `(progn
     (define-key evil-normal-state-map (kbd ,(concat "SPC " k)) ,fn)
     (define-key evil-insert-state-map (kbd ,(concat "C-c " k)) ,fn)
     (define-key evil-visual-state-map (kbd ,(concat "C-c " k)) ,fn)))

(pp-macroexpand-expression '(define-evil-key2 "x" #'foo))
=> (progn
     (define-key evil-normal-state-map (kbd "SPC x") #'foo)
     (define-key evil-insert-state-map (kbd "C-c x") #'foo)
     (define-key evil-visual-state-map (kbd "C-c x") #'foo))

That said, I see no reason to use a macro for this. I recommend that you make this a function.

1
  • Thank you for the explanation, I got the point. The latest macro with progn works exactly as I wanted, but in my case it will be easier to write a function instead.
    – jubnzv
    Dec 7, 2021 at 9:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.