将nil视为通配符类型

时间:2013-07-05 16:10:36

标签: types lisp common-lisp clos

我正在编写一个Lisp程序,并试图对类型有点认真。我想有性能改进,但我更感兴趣的是使用类型注释来提高文档和安全性。问题是nil。到目前为止,我遇到了两个问题。

图表A:

>(defmethod foo ((bar bar-class) (quux quux-class))
   ...)

>(foo (make-instance 'bar-class) nil)
 ERROR: No applicable method, etcetera etcetera, because nil is not of type quux-class

图表B:

(defmethod initialize-instance :after ((f foo) &rest args)
  "Initialize the grid to be the right size, based on the height and width of the foo."
  (declare (ignorable args))
  (setf (slot-value f 'grid) (make-array (list (width f) (height f))
                                         :element-type 'foo-component
                                         :adjustable nil
                                         :initial-element nil)))

style-warning: 
  NIL is not a FOO-COMPONENT.

这里的最佳做法是什么?到目前为止,我所拥有的唯一具有远程洞察力的想法是使用null object pattern并拥有(defclass nil-quux-class (quux-class) ...)(defclass nil-foo-component (foo-component) ...),但这似乎充其量只是hacky。我不确定为什么,但确实如此。坦率地说,我不习惯在CLOS中设计模式化的变通方法:)

2 个答案:

答案 0 :(得分:5)

(A)当您使用foo nilquux时,您希望发生什么? 参数

如果你什么都不想发生那么

(defmethod foo ((bar bar-class) (quux null))
  nil)

会把你排除在外。

如果你想要调用相同的代码,就好像你已经通过了一样 quux-class的实例,然后是:

(defmethod foo ((bar bar-class) (quux quux-class))
  (do-stuff bar quux))

(defmethod foo ((bar bar-class) (quux null))
  (do-stuff bar quux))

或:

(defmethod foo ((bar bar-class) quux)
  (unless (or (typep bar 'bar-class)
              (null bar))
    (error "Expected a bar-class or null, got ~s." quux))
  (do-stuff bar quux))

(B)你走了

(make-array size :element-type 'foo-component
                 :initial-element nil)

并且你的lisp实现指出了一个矛盾 - 初始元素不能同时为nilfoo-component。 (好吧,我猜 这取决于您的类型foo-component的样子。我在假设 它不包括null。)

您可以考虑:

(make-array :element-type '(or foo-component null)
            :initial-element nil)

但要注意:你希望你的口齿不清是通过了解一个来获得的 数组将包含foo-component s或nil s?优化?错误 检查你? (根据具体情况,您的里程可能会有所不同 你正在使用的lisp实现。)

答案 1 :(得分:4)

请注意,MAKE-ARRAY的元素类型不是真正的类型声明。它提示了Lisp实现数组应该能够存储哪种数据。然后它可能会选择专门的数组实现。

UPGRADED-ARRAY-ELEMENT-TYPE返回能够保存由typespec表示的类型的项目的最专业的数组表示的元素类型。

CL-USER 12 > (upgraded-array-element-type '(integer 0 100))
(UNSIGNED-BYTE 8)

上面意味着我请求一个数组,其整数元素在0到100之间。这个Lisp(这里是LispWorks)将给我一个元素类型为(unsigned-byte 8)的数组。

更多例子:

CL-USER 13 > (upgraded-array-element-type 'fixnum)
(SIGNED-BYTE 64)

CL-USER 14 > (upgraded-array-element-type 'complex)
T

CL-USER 15 > (defclass foo-component () ())
#<STANDARD-CLASS FOO-COMPONENT 402030196B>

CL-USER 16 > (upgraded-array-element-type 'foo-component)
T

T这意味着数组实际上将存储所有类型的数据对象。

CL-USER 17 > (upgraded-array-element-type '(or null foo-component))
T

CL-USER 20 > (make-array 2
                         :element-type 'foo-component
                         :initial-element (make-instance 'foo-component))
#(#<FOO-COMPONENT 40203B9A03> #<FOO-COMPONENT 40203B9A03>)

CL-USER 21 > (array-element-type *)
T

上面显示Lisp也会忘记最初请求的内容。我们实际上得到了一个元素类型T的数组,当我们要求它的元素类型时,它是T