help-gnu-emacs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Eval keymapp in a macros


From: Arthur Miller
Subject: Re: Eval keymapp in a macros
Date: Wed, 04 Aug 2021 12:52:09 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux)

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.




reply via email to

[Prev in Thread] Current Thread [Next in Thread]