5

I wonder which one should I use.

Here are the definitions from Emacs:

with-eval-after-load is a Lisp macro in ‘subr.el’.

(with-eval-after-load FILE &rest BODY)

Execute BODY after FILE is loaded.
FILE is normally a feature name, but it can also be a file name,
in case that file does not provide any feature.  See ‘eval-after-load’
for more details about the different forms of FILE and their semantics.

Probably introduced at or before Emacs version 24.4.
eval-after-load is a native-compiled Lisp function in ‘subr.el’.

(eval-after-load FILE FORM)

Arrange that if FILE is loaded, FORM will be run immediately afterwards.
If FILE is already loaded, evaluate FORM right now.
FORM can be an Elisp expression (in which case it’s passed to ‘eval’),
or a function (in which case it’s passed to ‘funcall’ with no argument).

If a matching file is loaded again, FORM will be evaluated again.

If FILE is a string, it may be either an absolute or a relative file
name, and may have an extension (e.g. ".el") or may lack one, and
additionally may or may not have an extension denoting a compressed
format (e.g. ".gz").

When FILE is absolute, this first converts it to a true name by chasing
symbolic links.  Only a file of this name (see next paragraph regarding
extensions) will trigger the evaluation of FORM.  When FILE is relative,
a file whose absolute true name ends in FILE will trigger evaluation.

When FILE lacks an extension, a file name with any extension will trigger
evaluation.  Otherwise, its extension must match FILE’s.  A further
extension for a compressed format (e.g. ".gz") on FILE will not affect
this name matching.

Alternatively, FILE can be a feature (i.e. a symbol), in which case FORM
is evaluated at the end of any file that ‘provide’s this feature.
If the feature is provided when evaluating code not associated with a
file, FORM is evaluated immediately after the provide statement.

Usually FILE is just a library name like "font-lock" or a feature name
like ‘font-lock’.

This function makes or adds to an entry on ‘after-load-alist’.

See also ‘with-eval-after-load’.

Unfortunately I did not find any info on it in the official manual.

According to ChatGPT (I know, not very reliable):

The key difference is that eval-after-load requires you to load the library explicitly using require before using it, while with-eval-after-load does not require a separate require statement because it automatically ensures that the library is loaded before executing the code inside the block. with-eval-after-load is more convenient and concise and is the preferred way to handle library loading and code execution in modern versions of Emacs.

But eval-after-load also does not require a separate require, so I do not know what is going on and which one to use.

Can anyone please the differences between those two?

8
  • I noticed that with-eval-after-load accepts many brackets (special expressions?) while with eval-after-load one can only get one. Are there any other differences? Commented Nov 6, 2023 at 18:21
  • 2
    ChatGPT is a terrible place to go for advice. Please just ignore it.
    – db48x
    Commented Nov 6, 2023 at 18:24
  • emacs.stackexchange.com/tags/elisp/info
    – Drew
    Commented Nov 6, 2023 at 19:58
  • 1
    Does this answer your question? How to customize a lazy-loaded variable?
    – Drew
    Commented Nov 6, 2023 at 20:03
  • 1
    i think the actual question you want to ask is: what is the difference between function and macro
    – shynur
    Commented Nov 7, 2023 at 4:56

1 Answer 1

9

Start with the first line of the help buffer:

with-eval-after-load is a Lisp macro in ‘subr.el’.
eval-after-load is a native-compiled Lisp function in ‘subr.el’.

See anything different about them? Yep, one is a function and the other is a macro. What is the difference? Well, the Lisp evaluator evaluates all of the function arguments before it calls the function, but macros are called by the compiler before any evaluation happens, so macro arguments get passed the unevaluated source code instead. Right away we can tell that with-eval-after-load is probably more convenient if you want some source code to be run, because putting that source in won’t involve any extra quoting. But eval-after-load is more convenient if you want to run a named function, because it is ok if the name of a function is evaluated.

There are two ways to go from here. The first is to look at the source code of the two. If you do, you’ll see that with-eval-after-load is by far the simplest of the two:

(defmacro with-eval-after-load (file &rest body)
  "Execute BODY after FILE is loaded.
FILE is normally a feature name, but it can also be a file name,
in case that file does not provide any feature.  See `eval-after-load'
for more details about the different forms of FILE and their semantics."
  (declare (indent 1) (debug (form def-body)))
  `(eval-after-load ,file (lambda () ,@body)))

It literally just wraps the code you give it up into a lambda, and then passes it along to eval-after-load. That saves you the effort of quoting the code or making it a lambda yourself. Nice.

You could also go look for other code that calls these two, and see what they do. You have the whole emacs lisp source code available, so there should be plenty of them. A random selection:

(eval-after-load "sendmail"
  '(progn
     (define-key mail-mode-map "\C-c\C-a" #'mail-abbrev-insert-alias)
     (define-key mail-mode-map "\e\t"        ; like completion-at-point
       #'mail-abbrev-complete-alias))) ;; FIXME: Use `completion-at-point'.

(eval-after-load 'tramp
  '(progn (lxd-tramp-add-method)
          (tramp-set-completion-function lxd-tramp-method lxd-tramp-completion-function-alist)))

(eval-after-load
    ,(if (symbolp pkg) `',pkg pkg)
  ',(macroexp-progn bindings))))

(eval-after-load 'elisp-mode
  (lambda ()
    (font-lock-add-keywords
     'emacs-lisp-mode
     '(("(\\(iter-defun\\)\\_>\\s *\\(\\(?:\\sw\\|\\s_\\)+\\)?"
        (1 font-lock-keyword-face nil t)
        (2 font-lock-function-name-face nil t))
       ("(\\(iter-\\(?:next\\|lambda\\|yield\\|yield-from\\)\\)\\_>"
        (1 font-lock-keyword-face nil t))))))

(eval-after-load "elec-pair"
  '(when (boundp 'electric-pair-inhibit-predicate)
     (dolist (buf (buffer-list))
       (with-current-buffer buf
         (when c-buffer-is-cc-mode
           (make-local-variable 'electric-pair-inhibit-predicate)
           (setq electric-pair-inhibit-predicate
                 #'c-electric-pair-inhibit-predicate))))))

Sheesh, who wrote all this crap? Every single one of these should be using with-eval-after-load so that they don’t have to quote anything. Virtually all of them need to do more than one thing, so they use progn. One of them uses a lambda, but it only does one thing! Not a single one of them just calls a named function. with-eval-after-load is just so much nicer:

(with-eval-after-load 'find-func
  (defvar find-function-regexp-alist)
  (add-to-list 'find-function-regexp-alist
               `(cl-defmethod . ,#'cl--generic-search-method))
  (add-to-list 'find-function-regexp-alist
               '(cl-defgeneric . cl--generic-find-defgeneric-regexp)))

(with-eval-after-load 'speedbar
  (Info-install-speedbar-variables))

(with-eval-after-load 'evil
  (evil-set-command-property 'lsp-find-definition :jump t)
  (evil-set-command-property 'lsp-find-implementation :jump t)
  (evil-set-command-property 'lsp-find-references :jump t)
  (evil-set-command-property 'lsp-find-type-definition :jump t))

(with-eval-after-load 'docker-tramp
  (warn (concat "Package `docker-tramp' has been obsoleted, "
                "please use integrated package `tramp-container'")))
(with-eval-after-load 'kubernetes-tramp
  (warn (concat "Package `kubernetes-tramp' has been obsoleted, "
                "please use integrated package `tramp-container'")))

See? All of those are really nice to read. Always use with-eval-after-load and you’ll be fine.

3
  • Thank you very much! This explained it all. Is there any good reason that Emacs was written entirely with functions rather than macros? Does it have to do with efficiency or something? Commented Nov 7, 2023 at 20:15
  • 1
    You cannot write everything with macros. Functions are the right tool 99.9% of the time, maybe more.
    – db48x
    Commented Nov 7, 2023 at 20:32
  • That's what I thought. Can you please name some advantages of using functions or direct me to some source? Commented Nov 8, 2023 at 20:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.