什么时候消除警告的好例子?
Good example of when to muffle warnings?
这个问题与之前 上的一个问题有些相关。我在抛出未定义变量警告的便捷宏中使用该函数。这个宏和函数:
(defmacro define-data (d body &optional doc)
(if (and doc (not (stringp doc))) (error "Documentation is not a string"))
`(let* ((d-str (string ',d))
(old-package *package*)
(*package* (if (find-package d-str) ;exists?
(find-package d-str) ;yes, return it
(make-package d-str)))) ;no, make it
;; Should we have an eval-when (:compile-toplevel) here?
(defparameter ,d ,body ,doc)
(export ',d old-package)
(define-column-names ,d)))
(defun define-column-names (d)
(maphash #'(lambda (key index)
(eval `(cl:define-symbol-macro ,key (cl:aref (columns ,d) ,index))))
(ordered-keys-table (slot-value d 'ordered-keys))))
旨在像 defparameter
,但通过定义为用户另外设置了一些细节:
- 一个名为
d
的包
- 当前包中的一个参数,其中包含将被
body
吸入的数据
- 包
d
中的符号宏,用于访问各个数据向量
如果我使用 REPL 中的 defparameter
,然后调用 define-column-names
,一切都很好。但是,当使用宏时,我得到:
; in: DEFINE-COLUMN-NAMES FOO
; (DEFINE-COLUMN-NAMES CL-USER::FOO)
;
; caught WARNING:
; undefined variable: CL-USER::FOO
我怀疑这是因为编译器无法知道在调用 define-symbol-macro
时 FOO 实际上会被定义。一切正常,但我不想让警告吓到用户,所以我想抑制它。不过我讨厌压制警告,所以我想来这里征求第二意见。
编辑:我已将答案标记为正确,因为它确实正确回答了问题。有关问题的答案,请参阅我的评论。
我不确定 define-columns-names
是如何工作的,所以我用 returns d
.
的存根函数替换了它
另请注意,您可以使用 check-type
并且应该尽量不要在生成的代码中注入符号,这会引入潜在的变量捕获,而使用 gensym
可以避免这种情况。
据我所知,您不能按照评论中的建议使用 eval-when
(有关详细信息,请参阅 Issue EVAL-WHEN-NON-TOP-LEVEL Writeup)。
但是如果我在调用周围声明该符号为特殊符号,我不会收到任何警告。
(defmacro define-data (d body &optional doc)
(check-type doc (or null string))
(check-type d symbol)
(let ((d-str (string d)))
(alexandria:with-gensyms (old-package)
`(let* ((,old-package *package*)
(*package* (if (find-package ,d-str) ;exists?
(find-package ,d-str) ;yes, return it
(make-package ,d-str)))) ;no, make it
(defparameter ,d ,body ,doc)
(export ',d ,old-package)
(locally (declare (special ,d))
(define-column-names ,d))))))
您扩展为对 define-column-names
的调用也有点奇怪,后者又会评估在运行时构建的表单。我认为在宏扩展期间可以做所有你想做的事情,但正如之前所说,你想要做的事情对我来说有点不清楚。我的想法是将 define-column-names
替换为:
,@(expand-column-names-macros d)
... 其中 expand-column-names-macros
构建 define-symbol-macro
表单列表。
我对标题中 'when to muffle warnings' 问题的回答是:如果这是你自己的代码,那么在任何情况下都不要 。如果是别人的代码,那就重写不警告,除非你不行。
至于解决这个问题,我还没有认真考虑过这个问题,但问题是你肯定希望 defparameter
位于 top-level 以便编译器可以看到它,并且如果它在 let
内,则不可能。但是你可以将它提升到顶层,因为它不依赖于 let
.
中的任何内容
然后我非常确定您希望宏的其余部分在编译时发生,因为您肯定希望 symbol-macros 在 compile-time 可用。所以第一个宏的尝试是(注意我已经修复了文档字符串的处理:(defparameter foo 1 nil)
是错误的):
(defmacro define-data (d body &optional doc)
(when (and doc (not (stringp doc)))
(error "Documentation is not a string"))
`(progn
(defparameter ,d ,body ,@(if doc (list doc) '()))
(eval-when (:compile-toplevel :load-toplevel :execute)
(let* ((d-str (string ',d))
(old-package *package*)
(*package* (if (find-package d-str) ;exists?
(find-package d-str) ;yes, return it
(make-package d-str)))) ;no, make it
(export ',d old-package)
(define-column-names ,d)))))
附带说明:尽管我认为以编程方式定义符号宏这一事实很难,因为 CL 出于某种原因将其排除在外,但我认为我个人会使用其他方法而不是这种方法,因为 [=15= 】 太可怕了。然而,这只是我:如果你想这样做,你确实需要 eval
我认为(这是真的很少见!)。
这个问题与之前
(defmacro define-data (d body &optional doc)
(if (and doc (not (stringp doc))) (error "Documentation is not a string"))
`(let* ((d-str (string ',d))
(old-package *package*)
(*package* (if (find-package d-str) ;exists?
(find-package d-str) ;yes, return it
(make-package d-str)))) ;no, make it
;; Should we have an eval-when (:compile-toplevel) here?
(defparameter ,d ,body ,doc)
(export ',d old-package)
(define-column-names ,d)))
(defun define-column-names (d)
(maphash #'(lambda (key index)
(eval `(cl:define-symbol-macro ,key (cl:aref (columns ,d) ,index))))
(ordered-keys-table (slot-value d 'ordered-keys))))
旨在像 defparameter
,但通过定义为用户另外设置了一些细节:
- 一个名为
d
的包
- 当前包中的一个参数,其中包含将被
body
吸入的数据
- 包
d
中的符号宏,用于访问各个数据向量
如果我使用 REPL 中的 defparameter
,然后调用 define-column-names
,一切都很好。但是,当使用宏时,我得到:
; in: DEFINE-COLUMN-NAMES FOO
; (DEFINE-COLUMN-NAMES CL-USER::FOO)
;
; caught WARNING:
; undefined variable: CL-USER::FOO
我怀疑这是因为编译器无法知道在调用 define-symbol-macro
时 FOO 实际上会被定义。一切正常,但我不想让警告吓到用户,所以我想抑制它。不过我讨厌压制警告,所以我想来这里征求第二意见。
编辑:我已将答案标记为正确,因为它确实正确回答了问题。有关问题的答案,请参阅我的评论。
我不确定 define-columns-names
是如何工作的,所以我用 returns d
.
另请注意,您可以使用 check-type
并且应该尽量不要在生成的代码中注入符号,这会引入潜在的变量捕获,而使用 gensym
可以避免这种情况。
据我所知,您不能按照评论中的建议使用 eval-when
(有关详细信息,请参阅 Issue EVAL-WHEN-NON-TOP-LEVEL Writeup)。
但是如果我在调用周围声明该符号为特殊符号,我不会收到任何警告。
(defmacro define-data (d body &optional doc)
(check-type doc (or null string))
(check-type d symbol)
(let ((d-str (string d)))
(alexandria:with-gensyms (old-package)
`(let* ((,old-package *package*)
(*package* (if (find-package ,d-str) ;exists?
(find-package ,d-str) ;yes, return it
(make-package ,d-str)))) ;no, make it
(defparameter ,d ,body ,doc)
(export ',d ,old-package)
(locally (declare (special ,d))
(define-column-names ,d))))))
您扩展为对 define-column-names
的调用也有点奇怪,后者又会评估在运行时构建的表单。我认为在宏扩展期间可以做所有你想做的事情,但正如之前所说,你想要做的事情对我来说有点不清楚。我的想法是将 define-column-names
替换为:
,@(expand-column-names-macros d)
... 其中 expand-column-names-macros
构建 define-symbol-macro
表单列表。
我对标题中 'when to muffle warnings' 问题的回答是:如果这是你自己的代码,那么在任何情况下都不要 。如果是别人的代码,那就重写不警告,除非你不行。
至于解决这个问题,我还没有认真考虑过这个问题,但问题是你肯定希望 defparameter
位于 top-level 以便编译器可以看到它,并且如果它在 let
内,则不可能。但是你可以将它提升到顶层,因为它不依赖于 let
.
然后我非常确定您希望宏的其余部分在编译时发生,因为您肯定希望 symbol-macros 在 compile-time 可用。所以第一个宏的尝试是(注意我已经修复了文档字符串的处理:(defparameter foo 1 nil)
是错误的):
(defmacro define-data (d body &optional doc)
(when (and doc (not (stringp doc)))
(error "Documentation is not a string"))
`(progn
(defparameter ,d ,body ,@(if doc (list doc) '()))
(eval-when (:compile-toplevel :load-toplevel :execute)
(let* ((d-str (string ',d))
(old-package *package*)
(*package* (if (find-package d-str) ;exists?
(find-package d-str) ;yes, return it
(make-package d-str)))) ;no, make it
(export ',d old-package)
(define-column-names ,d)))))
附带说明:尽管我认为以编程方式定义符号宏这一事实很难,因为 CL 出于某种原因将其排除在外,但我认为我个人会使用其他方法而不是这种方法,因为 [=15= 】 太可怕了。然而,这只是我:如果你想这样做,你确实需要 eval
我认为(这是真的很少见!)。