I'm writing a Lisp program and am trying to be a bit conscientious about types. I guess there's performance improvements, but I'm more interested in using type annotations for documentation and safety. The problem is nil
. I've run into two problems so far.
Exhibit 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
Exhibit 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.
What's the best practice here? So far the only remotely-insightful idea I've had is to use the null object pattern and have (defclass nil-quux-class (quux-class) ...)
and (defclass nil-foo-component (foo-component) ...)
, but that seems hacky at best. I'm not sure why, but it does. Frankly I'm not used to design patterny workarounds in CLOS :)
(A) What to you want to happen when you call foo
with nil
for the quux
argument?
If you want nothing at all to happen then
(defmethod foo ((bar bar-class) (quux null))
nil)
will sort you out.
If you want the same code to be called as if you had passed an
instance of quux-class
, then either:
(defmethod foo ((bar bar-class) (quux quux-class))
(do-stuff bar quux))
(defmethod foo ((bar bar-class) (quux null))
(do-stuff bar quux))
or:
(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) You've gone
(make-array size :element-type 'foo-component
:initial-element nil)
and your lisp implementation has pointed out a contradiction - the
initial elements can't be both nil
and foo-component
s. (Well, I guess
that depends on what your type foo-component
looks like. I'm assuming
it doesn't include null
.)
You might consider:
(make-array :element-type '(or foo-component null)
:initial-element nil)
but be aware: what do you want your lisp to gain from knowing that an
array will contain either foo-component
s or nil
s? Optimisation? Error
checking on your behalf? (Your mileage might vary, according to which
lisp implementation you're using.)