There is nothing wrong with having NIL represent null elements, but in case you really want to enforce the type, you can do it as follows.
The ALLOCATE-INSTANCE
generic function (but not INITIALIZE-INSTANCE
apparently) is specified to work with instances of STRUCTURE-CLASS
.
Forward declaration of function null-element
This function returns the null value for type element
.
With SBCL it is not strictly necessary if you compile all definitions at once (i.e. with compile-file
).
(declaim (ftype function null-element))
Define struct
The initform for values is a call to null-element
, so that the value satisfies the type when a new struct is formed. No constructor is defined for this struct to make it harder to build it manually (and to avoid naming problems).
(defstruct (element (:constructor nil))
(value 0 :type fixnum)
(next (null-element) :type element)
(prev (null-element) :type element))
Instantiate an element
Here the code allocates the structure, but does not initialize it. This avoids the problem you may have with sbcl where it automatically checks the type of its slots. Here, the content of the slots is undefined after allocation, but then they are set to values of the appropriate type (the element itself).
(defun make-element (value)
(let ((element (allocate-instance (find-class 'element))))
(prog1 element
(setf (element-value element) value
(element-next element) element
(element-prev element) element))))
Using a class, I would have called initialize-instance
instead but I don't think this is expected to work on all implementations. Using setf
to directly set the slots works.
Here I am going along with your question and have the element have links to itself.
Or better: Is there a way to create a start instance with next and prev referencing the instance itself?
You could also let the fields be (null-element)
, but this is only a matter of design choice.
Singleton null-element value
The call (null-element)
returns a sentinel value representing the null element. Here I allocate one instance of element
at load-time and return the same instance at each invocation. The actual value stored in the node is not important. As pointed out by RainerJoswig, the null element could be an instance of a struct where there is no such value.
(defun null-element ()
(load-time-value (make-element 0)))
(defun null-element-p (element)
(eq element (null-element)))
(make-element)
signals aTYPE-ERROR
because NIL is not the right type, that's why this is a problem. I tried with alocally
block around defstruct to lower the safety level to 0; it does break the environment if the slots are not set right away to a correct value, but if next and prev are set, then it works too – Archipenko