Common lisp 何时压制警告的好例子?

Common lisp 何时压制警告的好例子?,common-lisp,Common Lisp,这个问题与上一个问题有些关联。我在一个方便的宏中使用该函数,该宏抛出未定义的变量警告。此宏和函数: (定义数据(d主体和可选文档) (如果(和文档(不是(stringp文档)))(错误“文档不是字符串”)) `(让*((d-str(string',d)) (旧包装*包装*) (*包*(如果(查找包d-str);是否存在? (查找包裹d-str);是,返回 (制作d-str包);不,制作 在这里(:编译顶级)时,我们应该进行评估吗? (定义参数,d,主体,文档) (出口旧包装) (定义列名,d))

这个问题与上一个问题有些关联。我在一个方便的宏中使用该函数,该宏抛出未定义的变量警告。此宏和函数:

(定义数据(d主体和可选文档)
(如果(和文档(不是(stringp文档)))(错误“文档不是字符串”))
`(让*((d-str(string',d))
(旧包装*包装*)
(*包*(如果(查找包d-str);是否存在?
(查找包裹d-str);是,返回
(制作d-str包);不,制作
在这里(:编译顶级)时,我们应该进行评估吗?
(定义参数,d,主体,文档)
(出口旧包装)
(定义列名,d)))
(定义列名(d)
(maphash#'(lambda(键索引)
(eval`(cl:define symbol宏,key(cl:aref(columns,d),index)))
(有序键表(插槽值d'有序键)))
旨在类似于
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。一切正常,但我不想让警告吓坏用户,所以我想抑制它。不过我讨厌压制警告,所以我想我应该来这里征求第二个意见


    编辑:我已将答案标记为正确,因为它确实正确地回答了问题。有关此问题的答案,请参阅我的评论。

    我不确定
    定义列名的具体工作原理,因此我将其替换为返回
    d
    的存根函数

    还请注意,您可以使用
    检查类型
    ,并且应尽量不要在生成的代码中注入符号,这会引入潜在的变量捕获,而使用
    gensym
    可以避免这种情况

    据我所知,您不能按照您的评论建议在
    时使用
    eval(有关详细信息,请参阅)

    但是如果我在通话中声明符号是特殊的,我没有任何警告

    (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))))))
    
    还有一点奇怪的是,您将扩展为调用
    定义列名
    ,这反过来又对运行时生成的表单进行求值。我认为,在宏观扩张期间,你们可能想做什么就做什么,但正如前面所说的,我不太清楚你们想做什么。我想到的是将
    定义列名
    替换为:

    ,@(expand-column-names-macros d)
    

    。。。其中
    expand column names macros
    构建了一个
    define symbol macros
    表单的列表。

    我对标题中“何时隐藏警告”问题的回答是:如果是您自己的代码,那么在任何情况下都不会。如果它是其他人的代码,则重写它以不发出警告,除非您不能发出警告

    至于解决这个问题,我想得还不够仔细,但问题是您肯定希望
    defparameter
    处于顶级,这样编译器就可以看到它,如果它位于
    let
    中,它就不可能真正处于顶级。但是您可以简单地将其提升到顶级,因为它不依赖于
    let
    中的任何内容

    我很确定您希望宏的其余部分在编译时发生,因为您肯定希望符号宏在编译时可用。因此,尝试第一个宏将是(注意,我已经修复了docstring的处理:
    (defparameter foo 1 nil)
    不好):


    作为旁注:虽然我认为以编程方式定义符号宏很困难,因为CL出于某种原因忽略了这一点,但我个人认为我应该使用其他方法,而不是这样,因为
    eval
    太可怕了。这就是我,但是:如果你想这样做,你确实需要
    eval
    我认为(这是非常罕见的!)

    是的,你是对的
    (let…(eval when…)
    毫无意义:那只是不在顶层,你无法做到。在我写这条消息之前,我试着把一切可能的事情都转移到let之外,但它不起作用。在这种情况下,定义列名根本不起作用。我走了@coredump建议的路线,将define symbol宏移到define data宏中,但没有走得太远。我想这可能是值得再看一次的。我想知道的是,为什么定义列名只在defparameter在相同的词法范围内(即在let内)时才起作用好吧,这很难看,我也不确定为什么,但使用另一个eval似乎“修复”了这个问题:(eval“(define column names,df))。根据@rainer joshwig对前面问题的解释,我可以理解为什么需要定义列名,但我看不到这个。
    (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)))))