Elisp, alist and strings; type confusion

2019-07-05 00:26发布

问题:

I'm trying to publish an org-project as html, and automate the task with the following org project definition:

(defconst home (file-name-directory (or load-file-name buffer-file-name)))

(require 'org-publish)
(setq org-publish-project-alist
      '(
        ;; add all the components here
        ;; *notes* - publishes org files to html
        ("org-notes"
         :base-directory (concat home "org/")
         :base-extension "org"  ; Filename suffix without dot
         :publishing-directory (concat home "../public_html/")
         :recursive t           ; includes subdirectories
         :publishing-function org-publish-org-to-html
         :headline-levels 4             ; Just the default for this project.
         :auto-preamble t
         :auto-sitemap t                ; generate automagically
         :sitemap-filename "sitemap.org"
         :sitemap-title "Sitemap"
         )

        ;; *static* - copies files to directories
        ("org-static"
         :base-directory (concat home "org/")
         :base-extension "css\\|js\\|png\\|jpg\\|gif\\|pdf\\|mp3\\|ogg\\|swf"
         :publishing-directory (concat home "../public_html/")
         :recursive t
         :publishing-function org-publish-attachment
         )

        ;; *publish* with M-x org-publish-project RET emacsclub RET
        ("emacsclub" :components ("org-notes" "org-static"))
        ))

However, upon exporting the project, I get the error

Wrong type argument: stringp, (concat home "org")

From an Elisp perspective, what the heck is going on? Isn't the output of concat a string? In which case why is this failing? I try stringp on it's own with the concat argument and it returns true.

Something else I'm trying to get done, is have the whole project exported when this file gets evaluated. I've tried things like (command-execute org-publish-all) but it also complains of a wrong type argument. What can I use to get this done?

回答1:

The problem is that quoting the second argument in (setq org-publish-project-alist '(...)) means that nothing in that list structure will be evaluated. In other words, Emacs is telling you that the value (concat home "org") isn't a string: in fact it's a list of three elements (which if evaluated would give you a string).

One possible workaround might be to use the "backquote" or "quasiquote" mechanism, which is just like quote or ' but allows you to selectively splice in bits of evaluated Lisp code using , and ,@. (See (elisp)Backquote in the Info manual for more details). So, you could change your code above to something like

(setq org-publish-project-alist
  `(                            ; note ` instead of '
    ("org-notes"
     ;; Note commas , in front of code to evaluate
     :base-directory ,(concat home "org/")
     :base-extension "org"
     :publishing-directory ,(concat home "../public_html/")
     ....

Note that the unquoted pieces will get evaluated and spliced in to the list only once, when the (setq ...) form is evaluated: in other words, this won't help you if you need those values to change dynamically for different project directories. But since you're defining home as a constant maybe that doesn't matter?

PS: If you need to figure out in more detail where your wrong-type-argument errors are coming from, try doing M-x toggle-debug-on-error or evaluate (setq debug-on-error t) to get a detailed backtrace.