all messages for Emacs-related lists mirrored at yhetil.org
 help / color / mirror / code / Atom feed
* Eval keymapp in a macros
@ 2021-08-02 12:16 Arthur Miller
  2021-08-02 20:33 ` Michael Heerdegen
  0 siblings, 1 reply; 22+ messages in thread
From: Arthur Miller @ 2021-08-02 12:16 UTC (permalink / raw)
  To: help-gnu-emacs


I am not sure what I missunderstand here, but something I do. I am
trying to test if an object is a keymap, with keymapp in a
macro. Nothing fancy, just a small wrapper around define-key function.

However, I have to use eval to actually get keymapp to do something,
while I don't need to use eval with a listp or functionp predicate.
I am not so happy about using eval in there, so I would appreciate if
someone can enlighten me what I do wrong here (probably everything :)):

#+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



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  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
  0 siblings, 2 replies; 22+ messages in thread
From: Michael Heerdegen @ 2021-08-02 20:33 UTC (permalink / raw)
  To: help-gnu-emacs

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?  Anyway, `macroexpand' or `macroexpand-1' your call to see
what happens.

My hypothesis is that (cdr def) evals to a symbol (that is bound to a
keymap) - like `global-map', and not a keymap.  So the expansion of a
macro call would actually test e.g.

  (keymapp 'global-map)  --> nil

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

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.

Michael.




^ permalink raw reply	[flat|nested] 22+ messages in thread

* RE: [External] : Re: Eval keymapp in a macros
  2021-08-02 20:33 ` Michael Heerdegen
@ 2021-08-02 20:53   ` Drew Adams
  2021-08-03 21:20   ` Arthur Miller
  1 sibling, 0 replies; 22+ messages in thread
From: Drew Adams @ 2021-08-02 20:53 UTC (permalink / raw)
  To: Michael Heerdegen, help-gnu-emacs@gnu.org

> 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.

+1 for this working-backward-from-goal approach.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-02 20:33 ` Michael Heerdegen
  2021-08-02 20:53   ` [External] : " Drew Adams
@ 2021-08-03 21:20   ` Arthur Miller
  2021-08-04  0:18     ` Michael Heerdegen
  2021-08-04  4:54     ` Yuri Khan
  1 sibling, 2 replies; 22+ messages in thread
From: Arthur Miller @ 2021-08-03 21:20 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: help-gnu-emacs

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.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-03 21:20   ` Arthur Miller
@ 2021-08-04  0:18     ` Michael Heerdegen
  2021-08-04 10:52       ` Arthur Miller
  2021-08-04  4:54     ` Yuri Khan
  1 sibling, 1 reply; 22+ messages in thread
From: Michael Heerdegen @ 2021-08-04  0:18 UTC (permalink / raw)
  To: help-gnu-emacs

Arthur Miller <arthur.miller@live.com> writes:

> 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?

You are right.  What you get in the expansion is similar to what happens
here:

(let ((entry '([f11] . emacs-lisp-mode-map)))
  (define-key global-map (car entry) (cdr entry)))

which will also not "work".  You never evaluate the bindings - else you
would need to quote your command names.

With other words: you currently specify command names and keymaps in the
same way (as a symbol) - but the expansion needs to treat them
differently, obviously, since `define-key' always interprets a symbol as
a command name.  That can't work.  Your `keymapp' fix is an emergency
solution but it's not perfect: that test happens at compile time.  If
the keymap is not defined at compile time your compiled code will be
inappropriate.

The only solution for this is to rewrite your macro so that keymap and
command names are specified differently _in the call_.

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

I suggested to expand calls of the macro, not its definition ;-)

Unless you wanted to search for bugs in the implementation of
`defmacro'... but let's assume for now that your issue is caused by the
implementation of `with-key-map', and not by `defmacro' ;-)


> > 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.

Yes, see my example above: the (quoted) list gets evaluated, but not its
members, so you pass a symbol to `define-key' (what works for command
names but not for keymap names).


> 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.

Compiling is a good idea.  It would warn you if you misspelled a command
name (your version doesn't support that btw).

That concrete idea is cool but not really suitable for daily use, as you
already have found out: macro expansions can take damage after printing
and re-reading.

Why do you think you need that?  Why is normal code + normal compiling
not sufficient?


Michael.




^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-03 21:20   ` Arthur Miller
  2021-08-04  0:18     ` Michael Heerdegen
@ 2021-08-04  4:54     ` Yuri Khan
  2021-08-04  9:38       ` Arthur Miller
  1 sibling, 1 reply; 22+ messages in thread
From: Yuri Khan @ 2021-08-04  4:54 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Michael Heerdegen, help-gnu-emacs

On Wed, 4 Aug 2021 at 04:20, Arthur Miller <arthur.miller@live.com> wrote:

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

Two observations from the side:

* “with-*” naming is usually used for context manager macros. That is,
a macro initializes some resource or performs some temporary changes,
then executes the body, then does any necessary cleanup. You don’t
have any of that here.

* If you sacrifice a quote and a pair of braces, you can have your
typing saving as a simple function:

    (my-define-keys global-map
      '(("C-<insert>" . term-toggle)
        ("<insert>" . term-toggle-eshell)
        …))



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  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  0:03         ` Michael Heerdegen
  0 siblings, 2 replies; 22+ messages in thread
From: Arthur Miller @ 2021-08-04  9:38 UTC (permalink / raw)
  To: Yuri Khan; +Cc: Michael Heerdegen, help-gnu-emacs

Yuri Khan <yuri.v.khan@gmail.com> writes:

> On Wed, 4 Aug 2021 at 04:20, Arthur Miller <arthur.miller@live.com> wrote:
>
>> 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))
>
> Two observations from the side:
>
> * “with-*” naming is usually used for context manager macros. That is,
> a macro initializes some resource or performs some temporary changes,
> then executes the body, then does any necessary cleanup. You don’t
> have any of that here.

Yes indeed, I am aware, back when I named it I was thinking like "with
this keymap do this ..." so I actually crapped on the convention indeed.
I agree with you, and since I have rewrote it, to use a list of pairwise
elements instead of cons I have also renamed it to "defkeys", to
actually shorten the name, but it also does not violate the convention.

> * If you sacrifice a quote and a pair of braces, you can have your
> typing saving as a simple function:
>
>     (my-define-keys global-map
>       '(("C-<insert>" . term-toggle)
>         ("<insert>" . term-toggle-eshell)
>         …))

I know, but I wish the least noise as possible :-):

(defmacro defkey (mapname &rest body)
  `(let ((defs '(,@body)))
     (while defs
       (define-key ,mapname
         (if (vectorp (car defs))
             (car defs)
           (read-kbd-macro (car defs)))
         (if (or (listp (cadr defs))
                 (functionp (cadr defs)))
             (cadr defs)
           (if (eval`(keymapp ,(cadr defs)))
               (eval (cadr defs)))))
       (setq defs (cddr defs)))))



Than the usage looks like this

(setq pkg-ops-map
  (let ((map (make-sparse-keymap "Packages")))
    (defkey 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))

(defkey global-map "C-c p" pkg-ops-map)

(defkey global-map "s-1" pkg-ops-map)
(defkey global-map "s-1" list-packages)
(defkey global-map "s-1"  ("list" . list-packages))


Or the init file exammple:


(defkey global-map
  ;; 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)


I just would like to understand why order of lisp/functionp matters and
how can I get read of those two eval's, if possible.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-04  0:18     ` Michael Heerdegen
@ 2021-08-04 10:52       ` Arthur Miller
  2021-08-04 23:56         ` Michael Heerdegen
  0 siblings, 1 reply; 22+ messages in thread
From: Arthur Miller @ 2021-08-04 10:52 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: help-gnu-emacs

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Arthur Miller <arthur.miller@live.com> writes:
>
>> 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?
>
> You are right.  What you get in the expansion is similar to what happens
> here:
>
> (let ((entry '([f11] . emacs-lisp-mode-map)))
>   (define-key global-map (car entry) (cdr entry)))
>
> which will also not "work".  You never evaluate the bindings - else you
> would need to quote your command names.
>
> With other words: you currently specify command names and keymaps in the
> same way (as a symbol) - but the expansion needs to treat them
> differently

Yes, thank you. I think that is the key here. You put a finger on it as
they say. The macro itself is a little dsl that has to do stuff with
list of symbols. It is a bit similar situation to cl-loop I guess.

>                                    Your `keymapp' fix is an emergency
> solution but it's not perfect: that test happens at compile time.  If
> the keymap is not defined at compile time your compiled code will be
> inappropriate.

I agree, but do I wish to pass name of undefined keymap to define-key?
We have seen a move from adding functions to hooks by name to adding
function objects. Is there a difference here?

> I suggested to expand calls of the macro, not its definition ;-)

Aha. I missed that one :).

> Unless you wanted to search for bugs in the implementation of
> `defmacro'... but let's assume for now that your issue is caused by the
> implementation of `with-key-map', and not by `defmacro' ;-)
>
>
>> > 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.
>
> Yes, see my example above: the (quoted) list gets evaluated, but not its
> members, so you pass a symbol to `define-key' (what works for command
> names but not for keymap names).
>
>
>> 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.
>
> Compiling is a good idea.  It would warn you if you misspelled a command
> name (your version doesn't support that btw).

Oh, there is a lost of things that can go wrong there :). I am
aware. Eval could get passed entire program to erase the disk for
example. That could be easily hacked to pass funtion objects, I just
don't want to use function objects here. It is my private use, so I
really prefer it this way. I was thinking of it when I originally wrote
it.

> That concrete idea is cool but not really suitable for daily use, as you
> already have found out: macro expansions can take damage after printing
> and re-reading.
>
> Why do you think you need that?  Why is normal code + normal compiling
> not sufficient?

I could have used define-key all the way, but I wanted to reduce the
noise and repetion when typing. Syntatic sugar I guess. There is not
more power in this, and unlike for example is case of cl-loop, just some,
more error prone, convenience so to say :):

(defmacro defkey (mapname &rest body)
  `(let ((defs '(,@body)))
     (while defs
       (define-key ,mapname
         (if (vectorp (car defs))
             (car defs)
           (read-kbd-macro (car defs)))
         (if (or (listp (cadr defs))
                 (functionp (cadr defs)))
             (cadr defs)
           (if (eval`(keymapp ,(cadr defs)))
               (eval (cadr defs)))))
       (setq defs (cddr defs)))))

(defkey global-map
  "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)

I think, it removes a lot of noise. Unfortuantely macros are
only way to do special forms in elisp and work with unavluated
arguments. While manual does not state explicitly that it is not
possible to write user special forms, unlike for exmaple some other Lisp
implementations, I don't see how anything but macros would be possible
in elisp.

I find it handy to work with unevaluated arguments. If you compare
lisp(s) to TCL where the situation with argument passing is inverted,
unevaluated arguments are default and evaluation is explictly asked for
when arguments are passed to functions, I sometimes think it is better
default. I am not sure yet though :).

It is an exercise and learning experience, in how much I can automate and
let computer do things for me. I am also learning lisp and elisp, and so
I tinker a lot with impossible stuff I "shouldn't" do. Of course there
are pitfalls and caveats, but that's expected. I use this is in my init
file, so I will pass it correct stuff, it is not some kind of general
framework, so for me personally, reduced error catching is acceptable in
this case. Since this is a learning experience too, I really wish to
understand what was going on there, so thank you for the help.




^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  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  0:03         ` Michael Heerdegen
  1 sibling, 1 reply; 22+ messages in thread
From: Stefan Monnier via Users list for the GNU Emacs text editor @ 2021-08-04 15:37 UTC (permalink / raw)
  To: help-gnu-emacs

>> * If you sacrifice a quote and a pair of braces, you can have your
>> typing saving as a simple function:
>>
>>     (my-define-keys global-map
>>       '(("C-<insert>" . term-toggle)
>>         ("<insert>" . term-toggle-eshell)
>>         …))
>
> I know, but I wish the least noise as possible :-):

In most cases, macros are best defined in 2 steps:

1- provide a function-based solution
2- provide a simple macro that expands to a call to the function-based
   solution, with a very simple massaging (typically adding a few quotes
   or wrapping with some `lambda`).

That also makes debugging easier.


        Stefan




^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-04 10:52       ` Arthur Miller
@ 2021-08-04 23:56         ` Michael Heerdegen
  2021-08-05  6:03           ` Arthur Miller
  0 siblings, 1 reply; 22+ messages in thread
From: Michael Heerdegen @ 2021-08-04 23:56 UTC (permalink / raw)
  To: Arthur Miller; +Cc: help-gnu-emacs

Arthur Miller <arthur.miller@live.com> writes:

> > Your `keymapp' fix is an emergency solution but it's not perfect:
> > that test happens at compile time.  If the keymap is not defined at
> > compile time your compiled code will be inappropriate.

I see now that I was wrong here - the test is performed at run-time.

> I agree, but do I wish to pass name of undefined keymap to define-key?

Not at run-time: when the `define-key' call is evaluated, the symbol
must be bound, else you would get an error.

But some way or the other something has to decide how to interpret a
symbol.  If you want to use the same notation for the cases, you need to
use some kind of heuristic: in theory a symbol might name a function and
might be bound to a keymap at the same time.

And instead of `eval' better use `bound-and-true-p' - you know that you
look at a symbol.  You could also check whether the `symbol-name' ends
with "...-map".

Michael.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  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  0:03         ` Michael Heerdegen
  2021-08-05  6:15           ` Arthur Miller
  1 sibling, 1 reply; 22+ messages in thread
From: Michael Heerdegen @ 2021-08-05  0:03 UTC (permalink / raw)
  To: Arthur Miller; +Cc: help-gnu-emacs

Arthur Miller <arthur.miller@live.com> writes:

> I know, but I wish the least noise as possible :-)

One disadvantage of that approach is that it makes it harder to find the
definitions later.  If you, years later, wonder where your key bindings
are defined, you need to remember what syntax you used.  Your habits
might have changed then.  That's something to keep in mind.

Michael.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-04 23:56         ` Michael Heerdegen
@ 2021-08-05  6:03           ` Arthur Miller
  2021-08-06  3:54             ` Michael Heerdegen
  0 siblings, 1 reply; 22+ messages in thread
From: Arthur Miller @ 2021-08-05  6:03 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: help-gnu-emacs

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Arthur Miller <arthur.miller@live.com> writes:
>
>> > Your `keymapp' fix is an emergency solution but it's not perfect:
>> > that test happens at compile time.  If the keymap is not defined at
>> > compile time your compiled code will be inappropriate.
>
> I see now that I was wrong here - the test is performed at run-time.

I discovered that yesterday too. I tried the new macro on my init file,
and it found several left-over bindings after some packages I don't
use any more.

>> I agree, but do I wish to pass name of undefined keymap to define-key?
>
> Not at run-time: when the `define-key' call is evaluated, the symbol
> must be bound, else you would get an error.

Of course, every symbold has to bound when evaled. This is a bit special
use case. I am always ensuring it is evaled in context where everythign
is defined.The use case for my init file is that I pass those stuff
always in 'with-eval-after-load' for some package. So it is always
symbols should be always defined otherwise I have bigger problem then
misspelled syntax. I also expand this myself so I just just write
define-key calls to my init file. However I also have this macro defined
for interpretter, so I can test and eval stuff while I hack my init file.

> But some way or the other something has to decide how to interpret a
> symbol.  If you want to use the same notation for the cases, you need to
> use some kind of heuristic: in theory a symbol might name a function and
> might be bound to a keymap at the same time.

Yes, it's 'lisp-2' ... I like that feature. I know a lot of people
prefer scheme version, but I think it is handy and also more efficient
with 2 namespaces.

> And instead of `eval' better use `bound-and-true-p' - you know that you
> look at a symbol.

Thanks. I can remove at least the eval in test with bound-and-true-p,
but I don't think I can remove the second eval, since I have to get
object the symbol is representing.

However I am getting false positives from keymapp, it accepts anything
seems like:

(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 `(bound-and-true-p ,(cdr def))
             (if `(keymapp ,(cdr def))
                 (eval (cdr def))
               (cdr def))))))

Instead I have to use to test for listp and functionp first:

(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 `(bound-and-true-p ,(cdr def))
           (if (or (listp (cdr def))
                   (functionp (cdr def)))
               (cdr def)
             (if `(keymapp ,(cdr def))
                 (eval (cdr def))))))))

And I can also remove last if, and just leave eval, keymapp does not
seems to cull anything out.

>                    You could also check whether the `symbol-name' ends
> with "...-map".

I could, there is not guarantee, since it is just a convention, but yes
I agree, I could have done that.

Anyway, thanks  you for the help and feedback. I do have some better
understanding after this.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  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
  0 siblings, 1 reply; 22+ messages in thread
From: Arthur Miller @ 2021-08-05  6:12 UTC (permalink / raw)
  To: Stefan Monnier via Users list for the GNU Emacs text editor
  Cc: Stefan Monnier

Stefan Monnier via Users list for the GNU Emacs text editor
<help-gnu-emacs@gnu.org> writes:

>>> * If you sacrifice a quote and a pair of braces, you can have your
>>> typing saving as a simple function:
>>>
>>>     (my-define-keys global-map
>>>       '(("C-<insert>" . term-toggle)
>>>         ("<insert>" . term-toggle-eshell)
>>>         …))
>>
>> I know, but I wish the least noise as possible :-):
>
> In most cases, macros are best defined in 2 steps:
>
> 1- provide a function-based solution
> 2- provide a simple macro that expands to a call to the function-based
>    solution, with a very simple massaging (typically adding a few quotes
>    or wrapping with some `lambda`).
>
> That also makes debugging easier.
>
Agree. When I wrote this more than a year ago, I wasn't yet red On
Lisp. Since then I have actually got (almost) through On Lisp and Let
Over Lambda, and have start doing so. If I wrote it today I would
probably wrote a defun to return something I evaluate in macro, and
I would use that macro as "entrance" so I skip quoting. But I had this
6-lines small macro to just save some typing, and thought it would be
easier to fix. Actually for my own use, this is from my init file

(defmacro with-key-map (mapname &rest body)
  `(let ((map (eval-and-compile (if (string-match-p "-map$" (symbol-name ',mapname))
				    (symbol-name ',mapname)
				  (concat (symbol-name ',mapname) "-map"))))
	 (defs '(,@body)))
     (dolist (def defs)
       (define-key (symbol-value (intern map))
	 (if (vectorp (car def)) (car def)
	   (read-kbd-macro (car def)))
         (cdr def)))))


I am still good enough with that one, because I don't have so much more
need that to define some bindings in few places, but it was a bit of
challenge to adapt to the use case as pointed in the reddit thread.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-05  0:03         ` Michael Heerdegen
@ 2021-08-05  6:15           ` Arthur Miller
  2021-08-06  3:18             ` Michael Heerdegen
  0 siblings, 1 reply; 22+ messages in thread
From: Arthur Miller @ 2021-08-05  6:15 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: help-gnu-emacs

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Arthur Miller <arthur.miller@live.com> writes:
>
>> I know, but I wish the least noise as possible :-)
>
> One disadvantage of that approach is that it makes it harder to find the
> definitions later.  If you, years later, wonder where your key bindings
> are defined, you need to remember what syntax you used.  Your habits
> might have changed then.  That's something to keep in mind.

You have no idea what I do in my init file :).

Yes, I agree, and it is confusing to other people to see stuff without
quotes and dashes, as I saw in that mentioned reddit thread.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-05  6:12           ` Arthur Miller
@ 2021-08-05 13:53             ` Stefan Monnier
  2021-08-05 16:04               ` Arthur Miller
  0 siblings, 1 reply; 22+ messages in thread
From: Stefan Monnier @ 2021-08-05 13:53 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Stefan Monnier via Users list for the GNU Emacs text editor

> (defmacro with-key-map (mapname &rest body)
>   `(let ((map (eval-and-compile (if (string-match-p "-map$" (symbol-name ',mapname))
> 				    (symbol-name ',mapname)
> 				  (concat (symbol-name ',mapname) "-map"))))
> 	 (defs '(,@body)))
>      (dolist (def defs)
>        (define-key (symbol-value (intern map))
> 	 (if (vectorp (car def)) (car def)
> 	   (read-kbd-macro (car def)))
>          (cdr def)))))

Your `map` variable above has a misleading name since it won't hold
a map but a string (the name of the variable holding the map, so you
need the "double indirection" thought `intern` first and `symbol-value`
afterwards to get at the map).

    (defun my--with-key-map (map defs)
      (dolist (def defs)
        (let ((key (car def)))
          (define-key map
                      (if (vectorp key) key (read-kbd-macro key))
                      (cdr def)))))
    
    (defmacro my-with-key-map (mapname &rest body)
      `(my--with-key-map ,(if (string-match-p "-map\\'" (symbol-name mapname))
                              mapname
                            (intern (format "%s-map" mapname)))
                         ',body))

Look ma!  No `eval-and-compile` and no `symbol-value`.


        Stefan


PS: Personally I'd recommend against the `string-match-p` dance, since
    all it saves you is the typing of `-map` but in exchange it prevents
    you from using your macro with keymaps that have a name that doesn't
    end in `-map` and it "obfuscates" the code a little, preventing
    things like ElDoc and Xref from understanding what's going on.




^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-05 13:53             ` Stefan Monnier
@ 2021-08-05 16:04               ` Arthur Miller
  2021-08-05 16:34                 ` Stefan Monnier
  0 siblings, 1 reply; 22+ messages in thread
From: Arthur Miller @ 2021-08-05 16:04 UTC (permalink / raw)
  To: Stefan Monnier
  Cc: Stefan Monnier via Users list for the GNU Emacs text editor

Stefan Monnier <monnier@iro.umontreal.ca> writes:

>> (defmacro with-key-map (mapname &rest body)
>>   `(let ((map (eval-and-compile (if (string-match-p "-map$" (symbol-name ',mapname))
>> 				    (symbol-name ',mapname)
>> 				  (concat (symbol-name ',mapname) "-map"))))
>> 	 (defs '(,@body)))
>>      (dolist (def defs)
>>        (define-key (symbol-value (intern map))
>> 	 (if (vectorp (car def)) (car def)
>> 	   (read-kbd-macro (car def)))
>>          (cdr def)))))
>
> Your `map` variable above has a misleading name since it won't hold
> a map but a string

yes, I know, that was just me being lazy, that wasn't ment for other
people, so I used shorter name for the variable that I would type later
on :).
> PS: Personally I'd recommend against the `string-match-p` dance, since
>     all it saves you is the typing of `-map` but in exchange it prevents
>     you from using your macro with keymaps that have a name that doesn't
>     end in `-map` and it "obfuscates" the code a little, preventing
>     things like ElDoc and Xref from understanding what's going on.

And same reason explains -map obfuscation :). I thought from the
beginning it was cool to omit it but after a while realized it looked a
bit dumb, so now when I am changing to ordinary list instead of cons, I
planned to drop that. I agree it obscures the code with not much of
repetitive typing removed.

>     (defun my--with-key-map (map defs)
>       (dolist (def defs)
>         (let ((key (car def)))
>           (define-key map
>                       (if (vectorp key) key (read-kbd-macro key))
>                       (cdr def)))))
>     
>     (defmacro my-with-key-map (mapname &rest body)
>       `(my--with-key-map ,(if (string-match-p "-map\\'" (symbol-name mapname))
>                               mapname
>                             (intern (format "%s-map" mapname)))
>                          ',body))
>
> Look ma!  No `eval-and-compile` and no `symbol-value`.

When I said I would write it differently, I was thinking in different
direction indeed. This is simpler than what I had in mind. However :-)
... if we drop the -map suffix thing, we have:

(defun my--with-key-map (map defs)
(dolist (def defs)
  (let ((key (car def)))
    (define-key map
      (if (vectorp key) key (read-kbd-macro key))
      (cdr def)))))

(defmacro with-key-map (mapname &rest body)
  `(my--with-key-map ,mapname ',body))

This still does not handle the case with indirect keymap:

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

The last line will eval, but at runtime gives error: Wrong type
argument: commandp, pkg-ops-map.

The "evals" came to fix that case.

I tried with keymmapp in my--with-key-map but it still won't work
without eval, I think for the reasons as Michael summarized.




^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-05 16:04               ` Arthur Miller
@ 2021-08-05 16:34                 ` Stefan Monnier
  2021-08-06  4:17                   ` Michael Heerdegen
  0 siblings, 1 reply; 22+ messages in thread
From: Stefan Monnier @ 2021-08-05 16:34 UTC (permalink / raw)
  To: Arthur Miller; +Cc: Stefan Monnier via Users list for the GNU Emacs text editor

> (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))
>
> The last line will eval, but at runtime gives error: Wrong type
> argument: commandp, pkg-ops-map.

You can circumvent the problem with

    (defalias 'pkg-ops-map pkg-ops-map)


-- Stefan




^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-05  6:15           ` Arthur Miller
@ 2021-08-06  3:18             ` Michael Heerdegen
  0 siblings, 0 replies; 22+ messages in thread
From: Michael Heerdegen @ 2021-08-06  3:18 UTC (permalink / raw)
  To: Arthur Miller; +Cc: help-gnu-emacs

Arthur Miller <arthur.miller@live.com> writes:

> Yes, I agree, and it is confusing to other people to see stuff without
> quotes and dashes, as I saw in that mentioned reddit thread.

The problem I mean is that when you later want to search where you bound
command XY, you can't just grep for define-key.*XY or so, it's harder to
find stuff afterwards.  So while your file gets marginally smaller it
also gets harder to navigate.  Seems most people come to the conclusion,
over the years, that such abbreviations are not worth it.  Happened to
me as well.  If you do this kind of thing in a lot of places you risk a
big mess in your init file.

Michael.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-05  6:03           ` Arthur Miller
@ 2021-08-06  3:54             ` Michael Heerdegen
  2021-08-12 20:28               ` Arthur Miller
  0 siblings, 1 reply; 22+ messages in thread
From: Michael Heerdegen @ 2021-08-06  3:54 UTC (permalink / raw)
  To: Arthur Miller; +Cc: help-gnu-emacs

Arthur Miller <arthur.miller@live.com> writes:

> > And instead of `eval' better use `bound-and-true-p' - you know that you
> > look at a symbol.
>
> Thanks. I can remove at least the eval in test with bound-and-true-p,
> but I don't think I can remove the second eval, since I have to get
> object the symbol is representing.

And exactly this is what `bound-and-true-p' returns.  But that doesn't
matter if you are actually having a problem like this:

> However I am getting false positives from keymapp, it accepts anything
> seems like:
>
> (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 `(bound-and-true-p ,(cdr def))
>              (if `(keymapp ,(cdr def))
>                  (eval (cdr def))
>                (cdr def))))))
>
> Instead I have to use to test for listp and functionp first:
>
> (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 `(bound-and-true-p ,(cdr def))
>            (if (or (listp (cdr def))
>                    (functionp (cdr def)))
>                (cdr def)
>              (if `(keymapp ,(cdr def))
>                  (eval (cdr def))))))))
>
> And I can also remove last if, and just leave eval, keymapp does not
> seems to cull anything out.

I think you have a problem here with these nested backquotes, not
everything you think gets evaluated.  Have a look at the expansion
(`macroexpand').

> Anyway, thanks you for the help and feedback. I do have some better
> understanding after this.

An honest opinion: Maybe the advice to start with a function is the best
one so far.  Else you will be wasting time for nothing.  Don't write
macro code and look what happens when you call it and then try to fix
what seems to be wrong.  You'll get crazy and not learn very much.  That
approach doesn't work for most human brains.

One cool trick for learning: make the macro expander a named function.

I'm using your first definition from above - it is equivalent to

#+begin_src emacs-lisp
(defmacro with-key-map (mapname &rest body)
  (apply #'my-with-key-map-expander mapname body))

(defun my-with-key-map-expander (mapname &rest body)
  `(dolist (def '(,@body))
     (define-key ,mapname
       (if (vectorp (car def)) (car def)
	 (read-kbd-macro (car def)))
       (if `(bound-and-true-p ,(cdr def))
           (if (or (listp (cdr def))
                   (functionp (cdr def)))
               (cdr def)
             (if `(keymapp ,(cdr def))
                 (eval (cdr def))))))))
#+end_src

While this is semantically exactly the same and doesn't make much of a
difference you can now better study what the expander does: how it
transforms code (or forms) to code (to be evaluated).

E.g. now

  (with-key-map global-map ([f1] . make-frame-command))

will internally use an ordinary function call:

  (my-with-key-map-expander 'global-map '([f1] . emacs-list-mode-map))

to generate the expansion.  You can use the debugger, study the return
value etc.  Why does the code it returns not work as expected?  Would
you write the code like that?

To repeat: this is actually working in the wrong direction.  Better
start from the expected expansion, and then write an expander function
that generates that expansion.

Michael.



^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-05 16:34                 ` Stefan Monnier
@ 2021-08-06  4:17                   ` Michael Heerdegen
  2021-08-12 20:21                     ` Arthur Miller
  0 siblings, 1 reply; 22+ messages in thread
From: Michael Heerdegen @ 2021-08-06  4:17 UTC (permalink / raw)
  To: help-gnu-emacs

Stefan Monnier <monnier@iro.umontreal.ca> writes:

> You can circumvent the problem with
>
>     (defalias 'pkg-ops-map pkg-ops-map)

While that works, it is compensating the omission of quotes with the
insertion of other stuff (that even has side effects).  Not really
desirable...

Michael.




^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-06  4:17                   ` Michael Heerdegen
@ 2021-08-12 20:21                     ` Arthur Miller
  0 siblings, 0 replies; 22+ messages in thread
From: Arthur Miller @ 2021-08-12 20:21 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: help-gnu-emacs

Michael Heerdegen <michael_heerdegen@web.de> writes:

> Stefan Monnier <monnier@iro.umontreal.ca> writes:
>
>> You can circumvent the problem with
>>
>>     (defalias 'pkg-ops-map pkg-ops-map)
>
> While that works, it is compensating the omission of quotes with the
> insertion of other stuff (that even has side effects).  Not really
> desirable...

That was indeed really neat, just kind of hacky stuff I like :-).
However I don't either I will use it. Thanks anyway, both for the help.




^ permalink raw reply	[flat|nested] 22+ messages in thread

* Re: Eval keymapp in a macros
  2021-08-06  3:54             ` Michael Heerdegen
@ 2021-08-12 20:28               ` Arthur Miller
  0 siblings, 0 replies; 22+ messages in thread
From: Arthur Miller @ 2021-08-12 20:28 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: help-gnu-emacs

Michael Heerdegen <michael_heerdegen@web.de> writes:

Sorry for the little bit late answer. I didn't had time to look at this
immidiately, and then it just was left.

> Arthur Miller <arthur.miller@live.com> writes:
>
>> > And instead of `eval' better use `bound-and-true-p' - you know that you
>> > look at a symbol.
>>
>> Thanks. I can remove at least the eval in test with bound-and-true-p,
>> but I don't think I can remove the second eval, since I have to get
>> object the symbol is representing.
>
> And exactly this is what `bound-and-true-p' returns.  But that doesn't
Aha, I did a little bit of a noob thing there, and didn't checked the
docs. Just expected return to be a boolean and forgott that in elisp,
actually lots of stuff return the object itself, which is handy (like
`or', or `and' etc).

> matter if you are actually having a problem like this:
>
>> However I am getting false positives from keymapp, it accepts anything
>> seems like:
>>
>> (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 `(bound-and-true-p ,(cdr def))
>>              (if `(keymapp ,(cdr def))
>>                  (eval (cdr def))
>>                (cdr def))))))
>>
>> Instead I have to use to test for listp and functionp first:
>>
>> (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 `(bound-and-true-p ,(cdr def))
>>            (if (or (listp (cdr def))
>>                    (functionp (cdr def)))
>>                (cdr def)
>>              (if `(keymapp ,(cdr def))
>>                  (eval (cdr def))))))))
>>
>> And I can also remove last if, and just leave eval, keymapp does not
>> seems to cull anything out.
>
> I think you have a problem here with these nested backquotes, not
> everything you think gets evaluated.  Have a look at the expansion
> (`macroexpand').
>
>> Anyway, thanks you for the help and feedback. I do have some better
>> understanding after this.
>
> An honest opinion: Maybe the advice to start with a function is the best
> one so far.  Else you will be wasting time for nothing.  Don't write
> macro code and look what happens when you call it and then try to fix
> what seems to be wrong.  You'll get crazy and not learn very much.  That
> approach doesn't work for most human brains.

Yes, that is a very good advice, if anyone else than me is reading this
in the future.

> One cool trick for learning: make the macro expander a named function.
>
> I'm using your first definition from above - it is equivalent to
>
> #+begin_src emacs-lisp
> (defmacro with-key-map (mapname &rest body)
>   (apply #'my-with-key-map-expander mapname body))
>
> (defun my-with-key-map-expander (mapname &rest body)
>   `(dolist (def '(,@body))
>      (define-key ,mapname
>        (if (vectorp (car def)) (car def)
> 	 (read-kbd-macro (car def)))
>        (if `(bound-and-true-p ,(cdr def))
>            (if (or (listp (cdr def))
>                    (functionp (cdr def)))
>                (cdr def)
>              (if `(keymapp ,(cdr def))
>                  (eval (cdr def))))))))
> #+end_src
>
> While this is semantically exactly the same and doesn't make much of a
> difference you can now better study what the expander does: how it
> transforms code (or forms) to code (to be evaluated).
>
> E.g. now
>
>   (with-key-map global-map ([f1] . make-frame-command))
>
> will internally use an ordinary function call:
>
>   (my-with-key-map-expander 'global-map '([f1] . emacs-list-mode-map))
>
> to generate the expansion.  You can use the debugger, study the return
> value etc.  Why does the code it returns not work as expected?  Would
> you write the code like that?
>
> To repeat: this is actually working in the wrong direction.  Better
> start from the expected expansion, and then write an expander function
> that generates that expansion.

Indeed, and I agree. That was something like that I meant when I answered
in some earlier mail to Stefan. Anyway It all start with simple 6 lines
of very simple macro which worked out straightforward, so I was all the
time just "hacking" a single case it didn't worked for. But yes, in the
end, it is not very good approach.

Thank you for the help.



^ permalink raw reply	[flat|nested] 22+ messages in thread

end of thread, other threads:[~2021-08-12 20:28 UTC | newest]

Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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
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

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.