On the other hand, Cells should not change Lisp unnecessarily.
It would be easy enough to do something such as (cv +unbound+) or just (cv) and then have the internal sm-install function invoke slot-makunbound instead of forcing the slot to be bound. Then one does not need a special test for cell-boundp (which sounds wrong anyway in re transparency).
But what about a rule that runs across an unbound cell/slot? I should think that does not generate an unbound error, rather the slot mediated by the rule should in turn be made unbound.
I guess echoing works OK, tho anyway doing this on the GUI slots will not get far since existing echos are not testing for unbounditude.
The big hole is that slot-makunbound does not go through (setf /accessor/), and slot-makunbound-using-class is not portable, so we would need to lose some transparency and have an exported c-slot-makunbound function to make the slot unbound and kick off propagation (and confirm that the slot is c-variable mediated).
thoughts?
kt
Kenny Tilton wrote:
Thomas F. Burdick wrote:
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?
It might be the Dark Path of a long, well-respected, misbegotten tradition of collapsing two attributes into one, creating /extra/ work to save a slot. The first of the two slots is 'sign-in-status", with three values: not-yet, failed, or succeeded. Then there is another slot, which indicates who signed in (iff successful). We programmers have a long heritage and habit of collapsing two values into one to save memory or disk space (remember those?) by using some trick such as "use nil for failure, unbounditude for not-yet, non-nil bound for the user", but my experience has been that life gets a lot easier if I just let the diff attributes be diff slots.
It is certainly tempting to "save one slot", but, again, my experience has been that the consequent overloading saves a slot at the expense of forever complexifying the code. I can't just say (ecase (sign-in-status self) (:not-yet..)(:failed..)(:cool...))... I have to detect one expected value with 'cell-unboundp and the other two with ecase, every place in the code I need to access the user.
kt