2

Org-mode export backends are defined as instances of the cl structure org-export-backend:

(cl-defstruct (org-export-backend (:constructor org-export-create-backend)
                  (:copier nil))
  name parent transcoders options filters blocks menu)

The documentation string of cl-defstruct says:

This macro defines a new data type called NAME that stores data in SLOTs. It defines a make-NAME constructor, a copy-NAME copier, a NAME-p predicate, and slot accessors named NAME-SLOT. You can use the accessors to set the corresponding slots, via setf.

Each SLOT may instead take the form (SNAME SDEFAULT SOPTIONS…), where SDEFAULT is the default value of that slot and SOPTIONS are keyword-value pairs for that slot. Currently, only one keyword is supported, :read-only. If this has a non-nil value, that slot cannot be set via setf.

The options slot of org-export-backend is not marked with the keyword :read-only.

Therefore setting backend options with cl-pushnew should work as in the next example Elisp snippet:

(require 'cl-lib)
(with-eval-after-load 'ox-ascii
    (cl-pushnew
     '(:ascii-upcase-title nil "ascii-upcase-title" 'org+-ascii-upcase-title)
     (org-export-backend-options (org-export-get-backend 'ascii))))

But this does not work reliable. I had to remove it from an answer to a question about the ox-ascii export plugin.

If one calls M-x load-library RET ox-ascii RET after this setting, one gets the error

let*: Symbol’s function definition is void: \(setf\ org-export-backend-options\)

This error presents itself with the following message if one tries to export with org-export-dispatch:

Problems while trying to load export back-end ‘ascii’

What does cause this error? How can I prevent it?

Tobias
  • 32,569
  • 1
  • 34
  • 75

1 Answers1

3

Macroexpansion of the body of with-eval-after-load happens eagerly, i.e. before ox-ascii is loaded. So at that time, the slot is not defined and the setf/cl-pushnew macroexpander doesn't know what to do and chooses a default setter which is not the one that cl-defstruct will define.

Better move that code to a separate file:

(require 'ox-ascii)
(cl-pushnew
 '(:ascii-upcase-title nil "ascii-upcase-title" 'org+-ascii-upcase-title)
 (org-export-backend-options (org-export-get-backend 'ascii))))

and then load it when ox-ascii gets loaded:

(with-eval-after-load 'ox-ascii (load <myfile>))
Stefan
  • 26,154
  • 3
  • 46
  • 84
  • 1
    What about just using `eval-after-load` here? Or even `(eval-after-load "..." '(byte-compile ...))` – Drew Mar 04 '20 at 01:09
  • @Drew: if your stomach can take it, be my guest. – Stefan Mar 04 '20 at 02:09
  • @Drew Thanks Drew, I think your comment gives a hint for a solution in the init file. There one could just suspend the macro expansion until evaluation. I.e.: `(eval-after-load 'ox-ascii '(cl-pushnew ...))`. Is that okay? – Tobias Mar 04 '20 at 04:14
  • @Tobias: Yes, that's what I meant. Not sure what you mean by "Is that okay?". – Drew Mar 04 '20 at 04:36
  • @Drew Looks like the right magic formula is `(eval-after-load '(eval '(cl-pushnew ...)))`. With `(eval-after-load '(cl-pushnew ...))` alone one is still hit by the eager expansion problem. – Tobias Mar 04 '20 at 06:55