Kenny Tilton writes:
if you want to send me something with these two amendments I will manually merge with the Cells I have here, currently a subdirectory of Cello, and then future Cello/Cells releases will have that. I guess I am still leaning towards that structure, in which Cells can be accessed indepenendently but from within the larger Cello context.
Coolio, let's get it in CVS, over in Cello-land, then.
I tried to add unbound-cells support to Cells itself, and I think it works, but I wasn't expecting it to. In short:
(defmodel adder () ((x :accessor adder-x :initform (cv)) (y :accessor adder-y :initform (cv)) (sum :accessor adder-sum :initform (c? (+ (^adder-x) (^adder-y))))))
(defparameter >>adder (to-be (make-instance 'adder)))
(setf (adder-x >>adder) 0)
The above happily works. It doesn't trigger the rule until I do:
(adder-sum >>adder) => <unbound-cell error>
Am I crazy, or shouldn't it have given me that when I set adder-x to 0? Even if I am crazy, and that should work fine, one change does need to be made: cells doesn't currently catch the unbound-cell errors, because I wasn't sure where in the propagation code to put it.
Here are my changes:
========== in cells.lisp (defconstant +unbound+ '+unbound+)
(define-condition unbound-cell (unbound-slot) ())
========== in cell-types.lisp (defmacro c-formula ((&rest keys &key lazy cyclic-p cyclic-value) &body forms) (declare (ignore lazy cyclic-p cyclic-value)) `(make-c-dependent :code ',forms :rule (c-lambda ,@forms) ,@keys))
(defmacro c-variable ((&rest keys &key cyclic-p) &optional (value +unbound+)) (declare (ignore cyclic-p)) `(make-c-variable :value ,value ,@keys))
(defmacro cv (&optional (defn +unbound+)) `(make-c-variable :value ,defn)) ;; use c-independent if need deferred execution
========== in defmodel.lisp =============== this isn't particularly related, it just quiets down =============== SBCL a little. ; ; ------- defclass --------------- (^slot-value ,model ',',slotname) ;
(progn (defclass ,class ,(or directsupers '(model-object));; now we can def the class ,(mapcar (lambda (s) (list* (car s) (let ((ias (cdr s))) ;; We handle accessor below (when (getf ias :cell t) (remf ias :reader) (remf ias :writer) (remf ias :accessor)) (remf ias :cell) (remf ias :cwhen) (remf ias :unchanged-if) ias))) (mapcar #'copy-list slotspecs)) (:documentation ,@(or (cdr (find :documentation options :key #'car)) '("chya"))) (:default-initargs ;; nil ok and needed: acl oddity in re not clearing d-i's sans this ,@(cdr (find :default-initargs options :key #'car))) (:metaclass ,(or (find :metaclass options :key #'car) 'standard-class)))
========== in md-slot-value.lisp =============== in defun md-slot-value ; this next bit is not written (c-relay-value <link> (etypecase slot-c...)) ; because that would link before accessing possibly an invalid ruled slot ; (during md-awaken), and after calculating it would propagate to users and ; re-enter this calculation. Switching the order of the parameters would ; also work, but we need to document this very specific order of operations ; anyway, can't just leave that to the left-right thing. ; (let ((slot-value (etypecase slot-c (null (bd-slot-value self slot-spec)) (c-variable (c-value slot-c)) (c-ruled (c-ruled-slot-value slot-c))))) (format *debug-io* "slot ~S of ~S is ~S" self slot-spec slot-value) (when (eql slot-value +unbound+) (error 'unbound-cell :instance self :name slot-spec)) (c-relay-value (when (car *c-calculators*) (c-link-ex slot-c)) slot-value)))
========== in model-object.lisp (defun c-install (self sn c) (assert (typep c 'cell)) (trc nil "installing cell" sn c) (setf (c-model c) self (c-slot-spec c) sn (md-slot-cell self sn) c (slot-value self sn) (when (typep c 'c-variable) (c-value c))) (when (eql (slot-value self sn) +unbound+) (slot-makunbound self sn)))
Phew! Okay, I'm making sure I only edit verion-controlled source from now on, so I can make Emacs do the differencing work for me.