也许这个问题太笼统了,不过我会尝试:
是否有关于通用Lisp类型的全面指南?
我对此主题感到困惑:
为什么将make-array
的:element-type
中声明的非原始类型提升为t
?是否有可能对实际声明的类型进行编译时或运行时检查?
为什么CLOS插槽定义的类型不能用作约束,而允许将任何类型的值放入插槽?再次,检查如何?
带有declare
的函数类型声明是相同的。它们只是对编译器的优化提示吗?
另外,我是否可以使用自定义类型说明符,包括前面提到的位置的satisfies
进行一些健壮的检查,或者只能将它们用于typep
e.t.c的显式检查?
如您所见,我头脑有些困惑,因此,非常感谢任何简洁的指南(或一组指南)。
我在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
在运行时检查类型会增加一些开销(特别是如果它是在循环中发生的),并且对于单个值可能会多次执行,因此默认情况下不会执行此操作。
(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}>)
如果希望函数始终检查场所的类型,而不考虑优化设置,则应手动使用
CHECK-TYPE
。
关于types - 常见口臭类型的综合指南,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37705393/