Elisp macro 宏如何调用参数?

(defgroup syncthing nil
  "Syncthing client customize group."
  :prefix "syncthing-"
  :group 'syncthing)

(defcustom syncthing-server "http://localhost"
  "The server of Syncthing."
  :type 'string
  :safe #'stringp
  :group 'syncthing)

(defcustom syncthing-port 8080
  "The port of Syncthing."
  :type 'number
  :safe #'numberp
  :group 'syncthing)

(defcustom syncthing-api-key "n9mrv83iyememn3sgbc7z291k6o07r"
  "The API key for Syncthing."
  :type 'string
  :safe #'stringp
  :group 'syncthing)


(defmacro syncthing--rest-api (method endpoint &rest arglist)
  "The Syncthing REST API macro to construct HTTP METHOD, ENDPOINT with ARGLIST."
  `(defun ,(intern (format "syncthing-api-%s-%s" method endpoint)) ,arglist
     (let ((url-request-method ,method)
           (url-request-extra-headers '(("X-API-Key" . ,syncthing-api-key))))
       (with-current-buffer
           (url-retrieve-synchronously
            (format "%s:%s%s" syncthing-server syncthing-port ,endpoint))
         (goto-char (point-min))
         (re-search-forward "^$")
         (let ((result (json-read)))
           result)))))

(defvar syncthing--http-rest-endpoints-alist
  '(("GET" . "/rest/system/browse")
    ("GET" . "/rest/system/config")
    ("GET" . "/rest/system/config/isync")))

(syncthing--rest-api "GET" "/rest/system/config")

(mapcar
 (lambda (pair)
   (syncthing--rest-api (car pair) (cdr pair))) ; <-- problem here.
 syncthing--http-rest-endpoints-alist)

请问,这里创建macro后,为上面单独一行能够正常生产函数。但是在 mapcar 里面却无法解开 (car pair)(cdr pair). 输出的结果是:

(syncthing-api-\(car\ pair\)-\(cdr\ pair\) syncthing-api-\(car\ pair\)-\(cdr\ pair\) syncthing-api-\(car\ pair\)-\(cdr\ pair\))

这是为什么?应该怎样正确使用?

宏就是不对参数求值。

你以为他在第五层,实际他在第一层,macro就只能看到(car pair) (cdr pair) 看不到计算之后的结果。

可以改成

(syncthing--rest-api "GET" "/rest/system/browse")
(syncthing--rest-api "GET" "/rest/system/config")
(syncthing--rest-api "GET" "/rest/system/config/isync")

一定要这样么?有没有别的办法?因为HTTP API列表会更新,所以想弄个elisp alist来存储API, 手动重复写有点麻烦。当然麻烦不是重点。

想到了一个奇葩的解决办法,用全局变量,我看了Info文档,似乎参数可以先eval的。然后我用如下的解决办法:

(defmacro syncthing--rest-api (method endpoint &optional body &rest arglist)
  "The Syncthing REST API macro to construct HTTP METHOD, ENDPOINT with BODY and ARGLIST."
  `(defun ,(intern (format "syncthing-api-%s-%s" (eval method) (eval endpoint))) ,arglist
     (let ((url-request-method ,method)
           (url-request-extra-headers '(("X-API-Key" . ,syncthing-api-key))))
       (with-current-buffer
           (url-retrieve-synchronously
            (format "%s:%s%s" syncthing-server syncthing-port ,endpoint))
         (goto-char (point-min))
         (re-search-forward "^$")
         (let ((result (json-read)))
           result))
       ,@body)))

;;; TODO remove this temporary setting after finished
(setq syncthing-api-key "n9mrv83iyememn3sgbc7z291k6o07r")


(defvar syncthing--http-rest-endpoints-alist
  '(("GET" . "/rest/system/browse")
    ("GET" . "/rest/system/config")
    ("GET" . "/rest/system/config/isync")))

(defvar syncthing--http-rest-endpoint nil
  "A temporary variable used in mapcar loop.")

(defun syncthing--http-rest-endpoints-initialize ()
  "Initialize HTTP RESTful endpoints API functions."
  (mapcar
   (lambda (pair)
     (setq syncthing--http-rest-endpoint pair)
     ;; (syncthing--rest-api "GET" "/rest/system/config")
     (syncthing--rest-api (car syncthing--http-rest-endpoint) (cdr syncthing--http-rest-endpoint)))
   syncthing--http-rest-endpoints-alist))