Warm tip: This article is reproduced from serverfault.com, please click

macros-Common Lisp中的动态类型规范?

(macros - Dynamic type specification in Common Lisp?)

发布于 2020-12-07 00:26:29

我知道你可以在编程时声明变量参数的类型:

(defun foo (a b) (declare (type integer a b))

然而; 我希望能够动态指定类型。我有一个宏,可以通过传递一个args-list来创建a列表(cons单元列表),例如:

(defalist foo (x y z))  
--> returns function whose body ~ (list (cons 'x x) (cons 'y y) (cons 'z z))

我还希望能够动态指定args的类型,就像你在defun宏中以编程方式生成的方式一样。我试过使用声明/声明,但仍然遇到问题-似乎SBCL需要在编译时指定实际的类型...我希望能够在a内defmacro执行以下操作:

 (mapcan #'(lambda (arg typ) (declare (type typ arg))) args-list types-list)

最好的方法是什么?

谢谢你的帮助!

Questioner
Flywheel
Viewed
0
tfb 2020-12-08 02:40:30

我不确定你所说的“动态”是什么意思。

如果你的意思是,你要提供类型规范在运行时动态(这将是标准意义PF“动态”我认为),那么你不能做到这一点declare(或者你不能没有evalcompile或一些等价的,我们就不去那里了)。相反,你需要使用check-typetypep或者一些相关的事情动态检查的类型。declare在函数定义中使用as的工作是允许编译器学习有关函数中类型的一些知识,这可能使编译器可以创建更正确,更仔细,更不仔细,更快的代码组合(其中哪些适用)取决于编译器和编译选项)。为此,必须在编译时知道这些类型。

但是,如果你真的想要的是能够在宏定义声明类型,那么宏扩展发生在(或之前)的编译时间,所以这些类型其实都是静态的类型声明:在defalist将要扩大信息的一些defun表格,你可以简单地declare为该表格添加一个合适的表格。

这是一个这样的宏(请注意,我不知道你defalist的意思:这就是我发明的),它允许你指定类型:

(defmacro defalist (name (&rest args/types) &body junk &key
                         (default-type t)
                         (checked-instead
                          ;; use explicit type checks if we're not
                          ;; using a Python-derived compiler.
                          #-(or SBCL CMUCL) t
                          #+(or SBCL CMUCL) nil))
  ;; Each argument should either be a name or (name type).
  (declare (ignore junk))               ;just to get indentation
  (assert (every (lambda (a/t)
                   (or (symbolp a/t)
                       (and (consp a/t)
                            (symbolp (first a/t))
                            (= (list-length a/t) 2))))
                 args/types)
      (args/types)
    "bad arguments ~A" args/types)
  (multiple-value-bind (args types)
      (loop for a/t in args/types
            collect (typecase a/t
                      (symbol a/t)
                      (cons (first a/t)))
            into the-args
            collect (typecase a/t
                      (symbol default-type)
                      (cons (second a/t)))
            into the-types
            finally (return (values the-args the-types)))
    `(defun ,name (,@args)
       ,(if checked-instead
            `(progn
               ,@(loop for a in args and tp in types
                       collect `(check-type ,a ,tp)))
          `(declare ,@(loop for a in args and tp in types
                            collect `(type ,tp ,a))))
       (list ,@(loop for a in args
                   collect `(cons ',a ,a))))))

现在

(defalist foo (a b c))

扩展到

(defun foo (a b c)
  (declare (type t a) (type t b) (type t c))
  (list (cons 'a a) (cons 'b b) (cons 'c c)))
(defalist foo (a b c)
  :default-type integer)

扩展到

(defun foo (a b c)
  (declare (type integer a) (type integer b) (type integer c))
  (list (cons 'a a) (cons 'b b) (cons 'c c)))

最后

(defalist foo ((a fixnum) (b float) c)
  :default-type integer)

扩展到

(defun foo (a b c)
  (declare (type fixnum a) (type float b) (type integer c))
  (list (cons 'a a) (cons 'b b) (cons 'c c)))

重要说明:在这个特定的宏中,它定义的函数中的声明肯定会帮助SBCL或CMUCL编译器为你检查参数类型,甚至可能帮助它们推断函数的返回类型(这似乎不是在实践中)。但是,例如,它们不会导致返回的列表以不同的方式表示。但是,很容易想到这样的宏,它的对应函数在类型声明更有用的地方做一些事情。即使对于此功能,你也可以添加返回类型的声明,这可能会有所帮助(当然,这可能会帮助SBCL / CMUCL进行更多的类型推断)。

如果你没有使用从CMUCL编译器派生的编译器,则像这样的声明可能会使代码的安全性降低。因此,宏会尝试检测到这一点,并在那些编译器中通过显式检查替换类型声明。可以使用checked-instead关键字手动控制