Common Lisp 中的动态类型规范?
Dynamic type specification in Common Lisp?
我知道您可以在编程时为变量参数声明一个类型:
(defun foo (a b) (declare (type integer a b))
但是;我希望能够动态指定类型。我有一个宏,它可以让我通过传递 args-list 创建 a-list(cons 单元格列表)...例如:
(defalist foo (x y z))
--> returns function whose body ~ (list (cons 'x x) (cons 'y y) (cons 'z z))
我还希望能够动态指定 args 的类型,就像您在 defun
中那样,但是使用宏以编程方式生成。我试过使用 declare/declaim 但我将 运行 留在问题中——似乎 SBCL 需要在编译时指定的实际类型...
我希望能够在 defmacro
内完成类似的操作:
(mapcan #'(lambda (arg typ) (declare (type typ arg))) args-list types-list)
最好的方法是什么?
感谢您的帮助!
我不太清楚你说的 'dynamic' 是什么意思。
如果你的意思是你想在 运行-time 动态地提供类型规范 (这将是 pf 'dynamic' I 的标准含义think),那么你不能用 declare
来做到这一点(或者你不能没有 eval
或 compile
或一些等效的东西,我们不要去那里)。相反,您需要使用 check-type
、typep
或一些相关的东西来动态检查类型。在函数定义中使用 declare
的工作是允许 编译器 了解函数中有关类型的一些东西,这可能允许它创建代码,它是一些组合更正确、更小心、更不小心、更快(哪一个适用取决于编译器和编译选项)。为此,必须在编译时知道这些类型。
但是,如果您真正想要的是能够在宏定义中声明类型,那么宏展开会在编译时(或之前)发生,所以这些类型实际上是静态类型声明: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 编译器为您检查参数类型,甚至可能帮助他们推断 return 类型功能(在实践中似乎并非如此)。但是,例如,它们不会导致列表 it returns 的表示有任何不同。然而,很容易想象宏有点像这个宏,其相应的函数在类型声明更有用的地方做一些事情。即使对于这个函数,您也许可以添加一个 return 类型的声明,这可能会有所帮助(当然它可能有助于 SBCL / CMUCL 进行更多类型推断)。
如果您不使用在 CMUCL 编译器上派生的编译器,那么像这样的声明可能会降低代码的安全性。因此,宏会尝试检测这一点,并且在那些编译器中,它会通过显式检查来替换类型声明。这可以使用 checked-instead
关键字手动控制。
我知道您可以在编程时为变量参数声明一个类型:
(defun foo (a b) (declare (type integer a b))
但是;我希望能够动态指定类型。我有一个宏,它可以让我通过传递 args-list 创建 a-list(cons 单元格列表)...例如:
(defalist foo (x y z))
--> returns function whose body ~ (list (cons 'x x) (cons 'y y) (cons 'z z))
我还希望能够动态指定 args 的类型,就像您在 defun
中那样,但是使用宏以编程方式生成。我试过使用 declare/declaim 但我将 运行 留在问题中——似乎 SBCL 需要在编译时指定的实际类型...
我希望能够在 defmacro
内完成类似的操作:
(mapcan #'(lambda (arg typ) (declare (type typ arg))) args-list types-list)
最好的方法是什么?
感谢您的帮助!
我不太清楚你说的 'dynamic' 是什么意思。
如果你的意思是你想在 运行-time 动态地提供类型规范 (这将是 pf 'dynamic' I 的标准含义think),那么你不能用 declare
来做到这一点(或者你不能没有 eval
或 compile
或一些等效的东西,我们不要去那里)。相反,您需要使用 check-type
、typep
或一些相关的东西来动态检查类型。在函数定义中使用 declare
的工作是允许 编译器 了解函数中有关类型的一些东西,这可能允许它创建代码,它是一些组合更正确、更小心、更不小心、更快(哪一个适用取决于编译器和编译选项)。为此,必须在编译时知道这些类型。
但是,如果您真正想要的是能够在宏定义中声明类型,那么宏展开会在编译时(或之前)发生,所以这些类型实际上是静态类型声明: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 编译器为您检查参数类型,甚至可能帮助他们推断 return 类型功能(在实践中似乎并非如此)。但是,例如,它们不会导致列表 it returns 的表示有任何不同。然而,很容易想象宏有点像这个宏,其相应的函数在类型声明更有用的地方做一些事情。即使对于这个函数,您也许可以添加一个 return 类型的声明,这可能会有所帮助(当然它可能有助于 SBCL / CMUCL 进行更多类型推断)。
如果您不使用在 CMUCL 编译器上派生的编译器,那么像这样的声明可能会降低代码的安全性。因此,宏会尝试检测这一点,并且在那些编译器中,它会通过显式检查来替换类型声明。这可以使用 checked-instead
关键字手动控制。