(defmacro with-mode-on (mode &rest body)
(declare (indent defun)
(doc-string 3))
`(let ((,(make-symbol (concat (symbol-name mode) "-p")) ,mode))
(unless ,(make-symbol (concat (symbol-name mode) "-p")) (,mode 1))
,@body
(unless ,(make-symbol (concat (symbol-name mode) "-p")) (,mode -1))))
(macroexpand '(with-mode-on icomplete-mode (message "ss")))
展开后的结果是,看上去似乎没什么问题
(let ((icomplete-mode-p icomplete-mode))
(unless icomplete-mode-p (icomplete-mode 1))
(message "ss")
(unless icomplete-mode-p (icomplete-mode -1)))
但是执行的时候报
(with-mode-on icomplete-mode (message "ss"))
let: Symbol’s value as variable is void: icomplete-mode-p
cireu
2
每个make-symbol生成的symbol都是独一无二(uninterned symbol)的,如果不这么做,就没法根本上保证用了gensym之后宏可以卫生,这和intern生成的symbol不同,intern生成的symbol会放入变量obarray保存起来。有
(eq (make-symbol "sym") (make-symbol "sym"))
;; => nil
(eq (intern "sym") (intern "sym"))
;; => t
正确的玩法是用let保存一个uninterned symbol到变量里,然后用这个变量。
(defmacro with-mode-on (mode &rest body)
(declare (indent defun)
(doc-string 3))
(let ((sym (make-symbol (concat (symbol-name mode) "-p"))))
`(let ((,sym ,mode))
(unless ,sym (,mode 1))
,@body
(unless ,sym (,mode -1)))))
2 个赞
cireu
3
对于这种把一个sexp当成表达式赋值到一个临时变量的用法,Emacs 24.3后引入了macroexp-let2
(defmacro with-mode-on (mode &rest body)
(declare (indent defun)
(doc-string 3))
(macroexp-let2 nil mode-p mode
`(progn
(unless ,mode-p (,mode 1))
,@body
(unless ,mode-p (,mode -1)))))
展开后(#:是uninterned symbol的读入形式,不要在意)
(let*
((#:mode-p icomplete-mode))
(progn
(unless #:mode-p
(icomplete-mode 1))
(message "ssa")
(unless #:mode-p
(icomplete-mode -1))))
我展开之后的结果似乎跟你的不太一样
(defmacro with-mode-on (mode &rest body)
(declare (indent defun)
(doc-string 3))
(macroexp-let2 nil mode-p mode
`(progn
(unless ,mode-p (,mode 1))
,@body
(unless ,mode-p (,mode -1)))))
(macroexpand '(with-mode-on icomplete-mode (message "ss")))
(let* ((mode-p icomplete-mode)) (progn (unless mode-p (icomplete-mode 1)) (message "ss") (unless mode-p (icomplete-mode -1))))
cireu
5
(let ((print-gensym t)
(print-circle nil))
(pp (macroexpand '(with-mode-on icomplete-mode (message "ssa")))))
其实这样更能说明问题
(let ((print-gensym t)
(print-circle t))
(pp (macroexpand '(with-mode-on-old icomplete-mode (message "ssa")))))
;; (let
;; ((#:icomplete-mode-p icomplete-mode))
;; (unless #:icomplete-mode-p
;; (icomplete-mode 1))
;; (message "ssa")
;; (unless #:icomplete-mode-p
;; (icomplete-mode -1)))
(let ((print-gensym t)
(print-circle t))
(pp (macroexpand '(with-mode-on icomplete-mode (message "ssa")))))
;; (let*
;; ((#1=#:mode-p icomplete-mode))
;; (progn
;; (unless #1#
;; (icomplete-mode 1))
;; (message "ssa")
;; (unless #1#
;; (icomplete-mode -1))))
1 个赞