Skip to content

Instantly share code, notes, and snippets.

@daviwil
Last active February 5, 2022 13:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save daviwil/3215f99db0ac0cb122a9a1850f400594 to your computer and use it in GitHub Desktop.
Save daviwil/3215f99db0ac0cb122a9a1850f400594 to your computer and use it in GitHub Desktop.

The issue with the code turns out to be a facet of how macros work. This macro accepts a parameter num, but based on macro eval rules, num itself is actually the original symbol you passed into the function since macro arguments are never evaluated before they are given to the macro body.

With backquoting you can extract the actual value of num, but you won’t be able to do it for the intern call because it’s being evaluated in the macro expansion pass (no access to the value of num). You would need to pass a number literal in for num for this to work, and that’s why your manual invocations were working.

(defmacro ct/tab-bar-select-action-define (num)
  `(defun ,(intern (format "ct/tab-bar-select-%d" num)) ()
     ,(format "Select tab %d by its absolute number." num)
     (interactive)
     (tab-bar-select-tab ,num)))

In this part of the code, the only problem is that %i was used, but %d is the appropriate specifier for integers:

(let (tab-number)
  (dotimes (i 9)
    (setq tab-number (+ 1 i))
    (message (format ">%d" tab-number))
    (ct/tab-bar-select-action-define tab-number)))

There’s still the problem that ct/tab-bar-select-action-define receives tab-number the symbol and not the value. The solution here is to make this macro actually take the number of actions to define and do the loop internally.

The final catch is that dotimes returns nil but a macro needs to return the expression(s) that it expands into, so I’m using mapcar and number-sequence to replicate the dotimes behavior and then wrap it in a progn so that the macro returns a single evaluatable expression:

(defmacro ct/tab-bar-select-action-define (num)
  `(progn
     ,@(mapcar (lambda (i)
                `(defun ,(intern (format "ct/tab-bar-select-%d" i)) ()
                   ,(format "Select tab %d by its absolute number." i)
                   (interactive)
                   (tab-bar-select-tab ,i)))
              (number-sequence 0 9))))

;; Defines actions 0 to 9
(ct/tab-bar-select-action-define 9)
@DivineDominion
Copy link

Thanks for checking this out!

Since passing in a parameter is so much trouble, inlining the defun into the loop probably is easiest, based on your changed here:

(mapcar (lambda (tab-number)
          (let ((funname (intern (format "ct/tab-bar-select-%d" tab-number)))
                (docstring (format "Select tab %d by its absolute number." tab-number)))
            (eval-expression `(defun ,funname ()
                                ,docstring
                                (interactive)
                                (tab-bar-select-tab ,tab-number)))))
        (number-sequence 1 9))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment