lee*_*ski 4 types common-lisp clos slots
也许这个问题太笼统了,不过我会尝试:是否有关于通用Lisp类型的全面指南?
我对此主题感到困惑:
为什么在申报非原始类型make-array的:element-type都提升到t?是否有可能对实际声明的类型进行编译时或运行时检查?
为什么CLOS插槽定义的类型不能用作约束,而允许将任何类型的值放入插槽?再次,检查如何?
函数类型声明与declare.. 相同,它们只是对编译器的优化提示吗?
另外,我可以使用自定义类型说明符,包括satisfies在前面提到的地方进行一些可靠的检查,还是只能将它们用于typepetc的显式检查?
如您所见,我的脑海有些混乱,因此,非常感谢任何简洁的指南(或一组指南)。
我在SBCL上工作,但也很高兴了解实现之间的差异。
如果您希望编译器实际执行类型,则需要告诉编译器进行安全性优化:
CL-USER> (declaim (optimize (safety 3)))
NIL
CL-USER> (defclass foobar () ())
#<STANDARD-CLASS COMMON-LISP-USER::FOOBAR>
CL-USER> (defun foo (a)
(make-array 1 :element-type 'foobar
:initial-contents (list a)))
FOO
CL-USER> (foo (make-instance 'foobar))
#(#<FOOBAR {1005696CE3}>)
CL-USER> (foo 12)
;=> ERROR
CL-USER> (declaim (ftype (function (integer integer) integer) quux))
(QUUX)
CL-USER> (defun quux (a b)
(+ a b))
QUUX
CL-USER> (quux 12 12)
24 (5 bits, #x18, #o30, #b11000)
CL-USER> (quux 12 "asd")
;=> ERROR
Run Code Online (Sandbox Code Playgroud)
在运行时检查类型会增加一些开销(特别是如果它在循环中发生),并且对于单个值可能会多次执行,因此默认情况下不会进行此操作。
(declaim (optimize (safety 3)))
(defun some-predicate-p (a)
(format t "~&Checking type...")
(integerp a))
(deftype foo () `(satisfies some-predicate-p))
(defclass bar ()
((foo :type foo :initarg :foo)))
(declaim (ftype (function (foo) list) qwerty))
(defun qwerty (foo)
(loop repeat 10 collecting (make-instance 'bar :foo foo)))
(qwerty 12)
; Checking type...
; Checking type...
; Checking type...
; Checking type...
; Checking type...
; Checking type...
; Checking type...
; Checking type...
; Checking type...
; Checking type...
; Checking type...
;=> (#<BAR {1003BCA213}> #<BAR {1003BCA263}> #<BAR {1003BCA2B3}>
; #<BAR {1003BCA303}> #<BAR {1003BCA353}> #<BAR {1003BCA3A3}>
; #<BAR {1003BCA3F3}> #<BAR {1003BCA443}> #<BAR {1003BCA493}>
; #<BAR {1003BCA4E3}>)
Run Code Online (Sandbox Code Playgroud)
如果希望某个功能始终检查场所的类型,而不考虑优化设置,则应CHECK-TYPE手动使用。
为什么在 make-array 的 :element-type 中声明的非原始类型被提升为 t?是否有可能对实际声明的类型进行编译时或运行时检查?
该:element-type参数是有一个实现可以选择阵列优化的内存布局-主要是为节省存储空间。这对于原始类型通常很有用。对于其他类型,大多数 Common Lisp 运行时将没有优化的存储实现,因此声明将没有任何有用的效果。
为什么 CLOS 槽定义的类型不能作为约束,允许将任何类型的值放入槽中?再次,支票呢?
一个实现可以做到这一点。
Clozure CL:
? (defclass foo () ((bar :type integer :initform 0 :initarg :bar)))
#<STANDARD-CLASS FOO>
? (make-instance 'foo :bar "baz")
> Error: The value "baz", derived from the initarg :BAR,
can not be used to set the value of the slot BAR in
#<FOO #x302000D3EC3D>, because it is not of type INTEGER.
Run Code Online (Sandbox Code Playgroud)
使用declare..的函数类型声明也是如此。它们只是对编译器的优化提示吗?
可以忽略带有声明的类型声明 - 例如在 Symbolics Genera 中,大多数声明将被忽略。不需要实现来处理它们。大多数实现至少会将它们解释为保证某些对象将属于该类型并为此创建优化代码 - 可能没有运行时检查和/或该类型的专用代码。但通常需要设置相应的优化级别(速度、安全、调试、...)
此外,从 CMUCL 的编译器(SBCL,...)派生的编译器可能会将它们用于某些编译时检查。
但是在 ANSI CL 标准中没有指定任何影响。该标准提供声明并将解释留给实现。