I have a couple models where I'd like to have unbound slots that
normally have cv cells in them. NIL is a valid value, so I can't just
say "nil is the invalid value." What I'm doing is using a special
invalid-value object when I'd normally have an unbound slot:
(defconstant +unbound+ '+unbound+)
(deftype %nil () '(or nil (eql +unbound+)))
(defmacro define-unbound-methods (&body slot-specs)
(loop for (class slot opt-accessor) in slot-specs
for accessor = (or opt-accessor slot)
collect `(defmethod ,accessor :around ((object ,class))
(let ((value (call-next-method)))
(if (eq value +unbound+)
(error 'slot-unbound :instance object :name ',slot)
value)))
into methods
finally (return `(progn ,@methods))))
(defun cv_ () (cv +unbound+))
(defmodel my-handler (araneida:handler)
((user-id :accessor user-id :initarg user-id :initform (cv_)
:documentation
"user id or NIL if the user didn't supply valid credentials")
(homepage :accessor homepage :initarg homepage
:initform (c?_ (aif (id (^user-id))
(make-homepage-for-user id)
(create-user-account))))))
(define-unbound-methods (my-handler userid))
(defmethod handle-request-authentication ((handler my-handler) method request)
(setf (user-id h) ...))
If user-id is unbound, it means that authentication hasn't been
performed yet, so it would be a program error to try to use its value
at that point.
It seems like it wouldn't be too hard to put the concept of unbound
slots into Cells itself, so that storing the unbound-slot value would
work fine, but reading it would signal a slot-unbound error. Add
cell-makunbound and cell-boundp, and it would be the normal CLOS
semantics again.
Does this sound like a good idea? Or is there a more idiomatically
Cells way of doing this, and I'm wandering too far down The Dark Path
of Lazyness?