biggrben · 2022-07-08 10:23
Many `repeat-mode` usage examples I have come across has code like this:
(defvar foo-map
(let ((map (make-sparse-keymap)))
(define-key map (kbd "n") #'next-line)
(define-key map (kbd "p") #'previous-line)
map))
(put 'next-line 'repeat-map 'foo-map)
(put 'previous-line 'repeat-map 'foo-map)
(repeat-mode)
I am trying to understand why have a `LET` expression to make the keymap and then have it evaluate to that keymap which is then assigned to another variable. Why not something more straightforward like this?
(defvar foo-map (make-sparse-keymap))
(define-key foo-map (kbd "n") #'next-line)
(define-key foo-map (kbd "p") #'previous-line)
(put 'next-line 'repeat-map 'foo-map)
(put 'previous-line 'repeat-map 'foo-map)
(repeat-mode)
Are there any advantages of the first coding style? Are there any disadvantages of the second coding style?
SlowValue · 2022-07-08 11:06
(defvar ...) in an elisp source code file, when the file is loaded, will only change the symbols value, if the symbol was not bound before.
So using this `let` construction within the `defvar` sexp ensures, that any customization already made to the map will be preserved. Reloading the elisp file won't change the map.
Edit example:
file `foo.el`
(defvar bar 'foo)
(defvar bar 'baz)
now load file `M-x load-file RET foo.el RET`
Now `bar` has the value `foo`.
Now do a `(setq bar 'moo)` -> `bar` has the value `moo`
Now load file again like shown above. `bar` still has the value `moo`
sminja · 2022-07-08 15:43
To clarify: both of OP's examples use `(defvar foo-map...)`, so `foo-map` won't be changed. Is your concern that the `(define-key ...)` lines will, in the second version, override customization? And that, in the first version, by putting them inside the `devar` this is prevented?
SlowValue · 2022-07-08 21:12
> To clarify: both of OP's examples use (defvar foo-map...), so foo-map won't be changed.
foo-map won't be cleared in both examples, that's true.
> the (define-key ...) lines will, in the second version, override customization?
Assume that the user removed keybinding "n" from foo-map, then the second version would reintroduce the "n" keybinding.
> And that, in the first version, by putting them inside the devar this is prevented?
yes
arthurno1 · 2022-07-09 01:18
Another reason is to use let satement in this context is that you will usually have one longer name for them map, like my-foo-mode-map. Instead of typing all that at each define-key statement, like
define-key my-foo-mode-map (...)
you can let-bind the variable name to something shorter, like just 'map' as in your example, and type that through map definition instead. Other than that, there are no other cons and pros, not what I am aware of. It is slightly more expensive due to call to let and creation of extra var, but that is negligible.
[deleted] · 2022-07-09 01:34
[deleted]
arthurno1 · 2022-07-09 02:03
> So using this let construction within the defvar sexp ensures, that any customization already made to the map will be preserved. Reloading the elisp file won't change the map.
Yes, that is certainly true due to how defvar works, but it seems to be rater a bi-product, than the purpose of this idiom. We don't really re-load elisp files many times. In a normal session, files are loaded once. It is rather while developing that we re-load them and re-eval them, but that is not the major use-pattern, so I am not sure if it would warrant such care to invent an entire idiom to ensure bindings are not changed when a file is reloaded. I am not sure which behavior is more useful.
While, I am not sure, it seems to be that this idiom is rather a convenience that grew up into an idiom. In Emacs 29 it has got an abstraction in form of defvar-keymap (in lisp/keymap.el).
However, I am not familiar with the history, so I don't know for sure. Op should probably ask on emacs-devel mailing list to get the best answer.
sminja · 2022-07-09 05:07
Gotcha, thanks!
_viz_ · 2022-07-09 06:54
Byte-compile the two snippets and see the difference. AFAIU, the former gets eval'ed to a keymap object during compile-time whilst in the latter, it happens during run-time.
SlowValue · 2022-07-11 08:43
> you can let-bind the variable name to something shorter
Good point.