all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
From: Arthur Miller <arthur.miller@live.com>
To: Michael Heerdegen <michael_heerdegen@web.de>
Cc: help-gnu-emacs@gnu.org
Subject: Re: Eval keymapp in a macros
Date: Tue, 03 Aug 2021 23:20:29 +0200	[thread overview]
Message-ID: <AM9PR09MB497719795B0E3C1FBEEED13296F09@AM9PR09MB4977.eurprd09.prod.outlook.com> (raw)
In-Reply-To: 87bl6fy4cf.fsf@web.de

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Arthur Miller <arthur.miller@live.com> writes:
>
>> #+begin_src emacs-lisp
>>
>> (defmacro with-key-map (mapname &rest body)
>>   `(let ((map ,mapname))
>>      (dolist (def '(,@body))
>>         (define-key map
>>       (if (vectorp (car def)) (car def)
>>         (read-kbd-macro (car def)))
>>       (if (or (listp (cdr def))
>>               (functionp (cdr def)))
>>           (cdr def)
>>         (if (eval `(keymapp ,(cdr def)))
>>             (eval (cdr def))))))))
>>
>> #+end_src
>
> Unfortunately you didn't show how you use it.  What's the purpose of
> your macro? 

Indeed, I didn't, I appologize.

>                                                What's the purpose of
> your macro?

It is just a simple wrapper for a define-key so I save some
typing. I have actually rewrote it to not use cons any more but read
ordinary list by pairwise elements, but that does not matter, the real
work done is still the same.

Here is example from my init file (with conses):

             (with-key-map global
                           ;; Window-buffer operations
                           ("C-<insert>"    . term-toggle)
                           ("<insert>"      . term-toggle-eshell)
                           ([f9]            . ispell-word)
                           ([S-f10]         . next-buffer)
                           ([f10]           . previous-buffer)
                           ([f12]           . kill-buffer-but-not-some)
                           ([M-f12]         . kill-buffer-other-window)
                           ([C-M-f12]       . only-current-buffer))
                           
My original macro didn't handle the case when an indirect keymap should
be passed to define-key, someone on reddit point it out with example below:

(setq pkg-ops-map
      (let ((map (make-sparse-keymap "Packages")))
        (with-key-map map
                      ("h" . '("describe" . describe-package))
                      ("a" . '("autoremove" . package-autoremove))
                      ("d" . '("delete" . package-delete))
                      ("i" . '("install" . package-install))
                      ("s" . '("selected" . package-install-selected-packages))
                      ("r" . '("refresh" . package-refresh-contents))
                      ("l" . '("list" . list-packages)))
        map))

(with-key-map global-map ("C-c p" . pkg-ops-map))

So I rewrote it like this:

(defmacro with-key-map (mapname &rest body)
  `(dolist (def '(,@body))
     (define-key ,mapname
       (if (vectorp (car def)) (car def)
	 (read-kbd-macro (car def)))
       (if (keymapp (cdr def))
           (eval (cdr def))
           (cdr def)))))

But I get error: Wrong type argument: commandp, pkg-ops-map
       
So I ended with the one I posted which works, but I wonder why, and I
don't really like it. I think you are correct about the reason. That is
what I think also, so that is why I used eval, because I need the keymap
object itself to pass to define-key, at least so I think. Maybe I am
wrong there? 

>              Anyway, `macroexpand' or `macroexpand-1' your call to see
> what happens.

I know. I am aware of macroexpand, I do use it. The above macro does
expand to what I wanted, or better to say to what I thought I wanted,
but it seems that it is not correct. Most correctly, it does not expand
so much, since dolist itself is a macro:

(insert (pp (macroexpand-1 
'(defmacro with-key-map (mapname &rest body)
  `(dolist (def '(,@body))
     (define-key ,mapname
       (if (vectorp (car def)) (car def)
	 (read-kbd-macro (car def)))
       (if (keymapp (cdr def))
           (eval (cdr def))
       (cdr def)))))
)))

(defalias 'with-key-map
  (cons 'macro
        #'(lambda
            (mapname &rest body)
            `(dolist (def '(,@body))
               (define-key ,mapname
                 (if (vectorp
                      (car def))
                     (car def)
                   (read-kbd-macro (car def)))
                 (if (keymapp (cdr def))
                     (eval (cdr def))
                   (cdr def)))))))

I have edited spaces for readability.

> Note that nothing in BODY is ever evaluated (it's behind a quote).

Stuff in dolist gets evaluated, because dolist itself is a macro.
The real body of function which is all in do list gets expanded by
dolist (or the real interested part by 'while' which seems to be a
special form) and then evaled by dolist. So dolist will actually call
define-key for me, and that is what seem to happend, because stuff gets
defined after I call the macro. I hope I understand correctly what is
going on there.

Also I have to test for listp and functionp in correct order. When I
switch orders then I get errors too, so I am not so happy about that
macro att all.

As a side note, this isn't really a macro that writes a function or
another macro and returns it. I have it partly to save myself typing,
and partly to skip overhead of macroexpansion when Emacs start. Byte
compiler will expand it when init file is byte compiled. Actually I
wrote a program to write my init file which does expansion when it outputs
code to init file but it is just another regression. Anyway these are
just side notes, the macro works or should work in interpretter too.

> My tip when writing a macro (do you really need one btw?): Write an
> example call and then the desired expansion down.  Only after that write
> down the macro implementation that offers exactly that expansion.  You
> can later do that in your head, but if you skip that step you get all
> the surprises and pitfalls that macros are known for.

Indeed, I completely agree.

Thank you for the answer and for trying to help me. I am really confused
what happends there, so I really appreciate if you (or anyone) can make
it a bit more clear.



  parent reply	other threads:[~2021-08-03 21:20 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-08-02 12:16 Eval keymapp in a macros Arthur Miller
2021-08-02 20:33 ` Michael Heerdegen
2021-08-02 20:53   ` [External] : " Drew Adams
2021-08-03 21:20   ` Arthur Miller [this message]
2021-08-04  0:18     ` Michael Heerdegen
2021-08-04 10:52       ` Arthur Miller
2021-08-04 23:56         ` Michael Heerdegen
2021-08-05  6:03           ` Arthur Miller
2021-08-06  3:54             ` Michael Heerdegen
2021-08-12 20:28               ` Arthur Miller
2021-08-04  4:54     ` Yuri Khan
2021-08-04  9:38       ` Arthur Miller
2021-08-04 15:37         ` Stefan Monnier via Users list for the GNU Emacs text editor
2021-08-05  6:12           ` Arthur Miller
2021-08-05 13:53             ` Stefan Monnier
2021-08-05 16:04               ` Arthur Miller
2021-08-05 16:34                 ` Stefan Monnier
2021-08-06  4:17                   ` Michael Heerdegen
2021-08-12 20:21                     ` Arthur Miller
2021-08-05  0:03         ` Michael Heerdegen
2021-08-05  6:15           ` Arthur Miller
2021-08-06  3:18             ` Michael Heerdegen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=AM9PR09MB497719795B0E3C1FBEEED13296F09@AM9PR09MB4977.eurprd09.prod.outlook.com \
    --to=arthur.miller@live.com \
    --cc=help-gnu-emacs@gnu.org \
    --cc=michael_heerdegen@web.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this external index

	https://git.savannah.gnu.org/cgit/emacs.git
	https://git.savannah.gnu.org/cgit/emacs/org-mode.git

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.