Kenny Tilton ktilton@nyc.rr.com writes:
nikodemus@random-state.net wrote:
This is quite off-topic for the thread, but I for one always cringe when I see Cells code due to C? and friends.
If the syntax were:
RULE-LAMBDA (binding) form*
or
RULE-LAMBDA binding form*
Garnet's KR had (I forget)-formula. But when one is /really/ using Cells, one is spraying CVs and C?s around all the time, and this is more a candidate for something like collapsing QUOTE into ' or FUNCTION into #' then it is expansion into big-huge-name useful at the application level where, as we all know, excessive abbreviation saves so very little and detracts so much from readability.
That said, if some contributor wanted to create c-formula and c-variable macros and add them to Cells I would not mind.
Okay, how are these for a start?
(defmacro cv (&optional (value +unbound+)) `(cells::make-c-variable :value ,value))
(defmacro c-formula ((&rest keys &key lazy cyclic-p cyclic-value) &body forms) (declare (ignore lazy cyclic-p cyclic-value)) `(cells::make-c-dependent :code ',forms :rule (c-lambda ,@forms) ,@keys))
(defmacro c-variable ((&rest keys &key cyclic-p) &optional (value nil valuep)) (declare (ignore cyclic-p)) `(cells::make-c-variable :value ,(if valuep value '+unbound+) ,@keys))
They don't capture all the options (yet), but syntactically I think I like it. I agree that cv and c? are so common they should have their own short abbreviations. However, c?_8... seems like maybe not the best road syntactically. This syntax has the advantage of not needing to export a new macro if more options are added later (not that I can think of any types of cells still missing, of the top of my head).
Actually, I just changed my code to use c-formula instead of c?_, and I like it. It even saves me two columns of horizontal space. I have no desire to change my c? forms, but it's not like my lazy formulas were one-liners anyway.
Thomas F. Burdick wrote:
Kenny Tilton ktilton@nyc.rr.com writes:
nikodemus@random-state.net wrote:
This is quite off-topic for the thread, but I for one always cringe when I see Cells code due to C? and friends.
If the syntax were:
RULE-LAMBDA (binding) form*
or
RULE-LAMBDA binding form*
Garnet's KR had (I forget)-formula. But when one is /really/ using Cells, one is spraying CVs and C?s around all the time, and this is more a candidate for something like collapsing QUOTE into ' or FUNCTION into #' then it is expansion into big-huge-name useful at the application level where, as we all know, excessive abbreviation saves so very little and detracts so much from readability.
That said, if some contributor wanted to create c-formula and c-variable macros and add them to Cells I would not mind.
Okay, how are these for a start?
(defmacro cv (&optional (value +unbound+)) `(cells::make-c-variable :value ,value))
(defmacro c-formula ((&rest keys &key lazy cyclic-p cyclic-value) &body forms) (declare (ignore lazy cyclic-p cyclic-value)) `(cells::make-c-dependent :code ',forms :rule (c-lambda ,@forms) ,@keys))
(defmacro c-variable ((&rest keys &key cyclic-p) &optional (value nil valuep)) (declare (ignore cyclic-p)) `(cells::make-c-variable :value ,(if valuep value '+unbound+) ,@keys))
looks ok. but...
1. Is there a definition for +unbound+ missing?
2. Maybe if I stare at it long enough it will come to me, but why not do on c-variable what is proposed for cv, viz.,:
&optional (value '+unbound+)
kt
Kenny Tilton writes:
Thomas F. Burdick wrote:
Okay, how are these for a start?
(defmacro cv (&optional (value +unbound+)) `(cells::make-c-variable :value ,value))
(defmacro c-formula ((&rest keys &key lazy cyclic-p cyclic-value) &body forms) (declare (ignore lazy cyclic-p cyclic-value)) `(cells::make-c-dependent :code ',forms :rule (c-lambda ,@forms) ,@keys))
(defmacro c-variable ((&rest keys &key cyclic-p) &optional (value nil valuep)) (declare (ignore cyclic-p)) `(cells::make-c-variable :value ,(if valuep value '+unbound+) ,@keys))
looks ok. but...
Is there a definition for +unbound+ missing?
Maybe if I stare at it long enough it will come to me, but why not do
on c-variable what is proposed for cv, viz.,:
&optional (value '+unbound+)
Hmm, maybe I should have edited these on their way out of my utils file. +unbound+ is from (defconstant +unbound+ '+unbound+), from my unbound-cells hack from before. As for (2), yeah, that would be simpler. It was a mental-cut-and-paste error, from having written a bunch of macros right before that, that needed the supplied-p value.
Thomas F. Burdick wrote:
Kenny Tilton writes:
Thomas F. Burdick wrote:
Okay, how are these for a start?
(defmacro cv (&optional (value +unbound+)) `(cells::make-c-variable :value ,value))
(defmacro c-formula ((&rest keys &key lazy cyclic-p cyclic-value) &body forms) (declare (ignore lazy cyclic-p cyclic-value)) `(cells::make-c-dependent :code ',forms :rule (c-lambda ,@forms) ,@keys))
(defmacro c-variable ((&rest keys &key cyclic-p) &optional (value nil valuep)) (declare (ignore cyclic-p)) `(cells::make-c-variable :value ,(if valuep value '+unbound+) ,@keys))
looks ok. but...
Is there a definition for +unbound+ missing?
Maybe if I stare at it long enough it will come to me, but why not do
on c-variable what is proposed for cv, viz.,:
&optional (value '+unbound+)
Hmm, maybe I should have edited these on their way out of my utils file. +unbound+ is from (defconstant +unbound+ '+unbound+), from my unbound-cells hack from before. As for (2), yeah, that would be simpler.
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. In fact, maybe there is a pattern here: every big huge library can choose one and only one big huge parent library which depends crucially on it, and likely will have only one plausible candidate parent. Wouldn't that be an interesting law of software architecture, if in fact the latter proved true? eg, cl-typesetting being a natural container for cl-pdf, and cello being a natural for cells.
kt
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.
Thomas F. Burdick writes:
(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))
*sigh* I thought I'd gotten all my debugging notes out of there.
Thomas F. Burdick wrote:
(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>
Add: (def-c-echo sum () (print (list self new-value old-value)))
...and you'll get the error when to-be runs.
I added your code. My c-install had evolved a little, so I just added the make-unbound. I see you /had/ handle things on the read side, so I am trying to remember what we had talked about on the propagation side:
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.
The above is confusing. Cells (with your additions) certainly does catch reads of unbound cells, as you noted once you accessed adder-sum.
kt
Kenny Tilton writes:
Thomas F. Burdick wrote:
(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>
Add: (def-c-echo sum () (print (list self new-value old-value)))
...and you'll get the error when to-be runs.
Hmm, I get the error once. That is:
(to-be (make-instance 'adder)) => <error> (to-be (make-instance 'adder)) => ADDER
I added your code. My c-install had evolved a little, so I just added the make-unbound.
Cool. What I sent should be fine alone, it has the capabilities of the hack I'd been using. However, it would be cool to keep going, and have support for making slots unbound:
I see you /had/ handle things on the read side, so I am trying to remember what we had talked about on the propagation side:
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.
The above is confusing. Cells (with your additions) certainly does catch reads of unbound cells, as you noted once you accessed adder-sum.
I meant "doesn't handle conditions of type CELLS:UNBOUND-CELL". This doesn't come up in what I have so far, because of the nascent-cells thing that was throwing me. But, let's say we add a method on SLOT-MAKUNBOUND-USING-CLASS (or add a c-slot-makunbound function). If I call (slot-makunbound >>adder 'x), it should set X back to being unbound, and kick off the rule for SUM.
At this point, something should handle the UNBOUND-CELL error, and make SUM unbound. Now, if I call ADDER-SUM, it should kick off SUM's rule again, and this time raise the UNBOUND-CELL error for X. I hadn't yet tried to figure out where to establish a handler for UNBOUND-CELL, because I was tripped out by my little example above.
capite?
Thomas F. Burdick wrote:
Kenny Tilton writes:
Thomas F. Burdick wrote:
(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>
Add: (def-c-echo sum () (print (list self new-value old-value)))
...and you'll get the error when to-be runs.
Hmm, I get the error once. That is:
(to-be (make-instance 'adder)) => <error> (to-be (make-instance 'adder)) => ADDER
Shut up! I can't wait to see this one. Gotta run out for a sec, but I'll look at this in a bit.
I added your code. My c-install had evolved a little, so I just added the make-unbound.
Cool. What I sent should be fine alone, it has the capabilities of the hack I'd been using. However, it would be cool to keep going, and have support for making slots unbound:
ok, that's where I thought you were going.
I meant "doesn't handle conditions of type CELLS:UNBOUND-CELL". This doesn't come up in what I have so far, because of the nascent-cells thing that was throwing me. But, let's say we add a method on SLOT-MAKUNBOUND-USING-CLASS (or add a c-slot-makunbound function). If I call (slot-makunbound >>adder 'x), it should set X back to being unbound, and kick off the rule for SUM.
right.
At this point, something should handle the UNBOUND-CELL error, and make SUM unbound.
well, I always get a kick out of digging and digging and finally there it is in all its simplicity:
(funcall (c-rule c) self)
...or something like that. So that would be where the trap for unbounditude would go.
Now, if I call ADDER-SUM, it should kick off SUM's rule again,..
Why? rules get run when dependencies change or the state is unknown (only after make-instance, except for lazy cells which have been invalidated but not recalculated). The unbounditude is effectively a cached result, so there is no need to kick off the rule.
and this time raise the UNBOUND-CELL error for X. I hadn't yet tried to figure out where to establish a handler for UNBOUND-CELL, because I was tripped out by my little example above.
capite?
kt
Kenny Tilton writes:
Thomas F. Burdick wrote:
At this point, something should handle the UNBOUND-CELL error, and make SUM unbound.
well, I always get a kick out of digging and digging and finally there it is in all its simplicity:
(funcall (c-rule c) self)
...or something like that. So that would be where the trap for unbounditude would go.
Now, if I call ADDER-SUM, it should kick off SUM's rule again,..
Why? rules get run when dependencies change or the state is unknown (only after make-instance, except for lazy cells which have been invalidated but not recalculated). The unbounditude is effectively a cached result, so there is no need to kick off the rule.
Good point. I was thinking we'd kick of the rule here, so the user could see which slot was ultimately responsible for the unbound error -- but we can just cache that information, hell, cache the condition itself, and signal it when needed.
Oops. After an error, *to-be-awakened* needs to be cleared in a protected form. That stuff is ugly, and I think Cells: The Next Generation will make that and other ugliness go away.
For now, try this:
(defun to-be (self) (trc "to-be> entry" self (md-state self))
(progn ;;wtrc (0 100 "to-be> entry" self (md-state self) (length *to-be-awakened*)) (when (eql :nascent (md-state self)) ;; formwithview to-be-primary :after => rv-stitch! => side-effects (let ((already *to-be-awakened*)) (setf *to-be-awakened* (nconc *to-be-awakened* (list self))) (unwind-protect (progn (trc nil "to-be deferring awaken" self) (kids self) ;; sick, just for side effect (unless already (trc nil "top to-be awakening deferred" self (length *to-be-awakened*)) (do* ((mds *to-be-awakened* (cdr mds)) (md (car mds) (car mds))) ((null mds)) (if (eql :nascent (md-state md)) (md-awaken md) (trc nil "not md-awakening non-nascent" md))))) (setf *to-be-awakened* nil))))) self)
kt
Kenny Tilton writes:
Oops. After an error, *to-be-awakened* needs to be cleared in a protected form. That stuff is ugly, and I think Cells: The Next Generation will make that and other ugliness go away.
For now, try this:
Aiiight, that worked. Now that my expectation of what should happen, and what is happening are in sync, I'll dive back in, and finish the job.
So ... uh, which approach do you think is worse: using a c-slot-makunbound function that works for both normal and cell-mediated slots; or including hacks to make MCL and CLISP go through slot-makunbound-using-class?
Thomas F. Burdick wrote:
Kenny Tilton writes:
Oops. After an error, *to-be-awakened* needs to be cleared in a protected form. That stuff is ugly, and I think Cells: The Next Generation will make that and other ugliness go away.
For now, try this:
Aiiight, that worked. Now that my expectation of what should happen, and what is happening are in sync, I'll dive back in, and finish the job.
So ... uh, which approach do you think is worse: using a c-slot-makunbound function that works for both normal and cell-mediated slots;
In this contrast between normal and cell-mediated, does normal mean a slot specified:
:cell nil
or a potentially cell-mediatable slot of an instance in which the slot is not in fact mediated by a cell?
:cell t :initform 42
The former would not hurt since we are trying hard to make Cell unbounditude work like CL's, tho I have as a bit of programmer-friendliness tended to respond "yo, this slot is specified ':cell nil'".
[aside: is it c-slot-makunbound or md-slot-makunbound? I have both in re slot-value. md- takes the slot name, looks for a cell, calls c- if it finds one. By that parallel we are talkin bout md-slot-value]
or including hacks to make MCL and CLISP go through slot-makunbound-using-class?
Hunh? MCL does not /have/ a slot-makunbound-using-class (he guessed based on MCL not exposing much of the MOP). How can you make MCL go thru what it does not have? ie, if (i am guessing) slot-makunbound is a function, how do you change its behavior? advise (which is one corner of Lisp I have never visited)?
Golly I wish the MOP were part of the standard.
kt
Kenny Tilton writes:
Thomas F. Burdick wrote:
So ... uh, which approach do you think is worse: using a c-slot-makunbound function that works for both normal and cell-mediated slots;
In this contrast between normal and cell-mediated, does normal mean a slot specified:
:cell nil
I still need to work on my Cells-related terminology; I mean this case. That way Cells-using programs could use the same makunbound for all objects, without worrying about where they came from.
[aside: is it c-slot-makunbound or md-slot-makunbound? I have both in re slot-value. md- takes the slot name, looks for a cell, calls c- if it finds one. By that parallel we are talkin bout md-slot-value]
Yeah, I guess it would be md-slot-makunbound, considering you get to the slot with md-slot-value.
or including hacks to make MCL and CLISP go through slot-makunbound-using-class?
Hunh? MCL does not /have/ a slot-makunbound-using-class (he guessed based on MCL not exposing much of the MOP). How can you make MCL go thru what it does not have? ie, if (i am guessing) slot-makunbound is a function, how do you change its behavior? advise (which is one corner of Lisp I have never visited)?
Golly I wish the MOP were part of the standard.
(No kidding). Well, MCL doesn't /ship/ with s-m-u-c ... but it does have enough of a MOP to write one. Then, once such a beast is written, slot-makunbound can be redefined to go through it. In fact, I have such a beast from my previous MCL adventures. Those with sensitive constitutions may want to skip this:
(defpackage :s-m-u-c-patch (:use :common-lisp :ccl))
(in-package :s-m-u-c-patch)
(unless (fboundp 'ccl::slot-makunbound-using-class)
(unless (fboundp '%slot-makunbound) (setf (symbol-function '%slot-makunbound) (symbol-function 'slot-makunbound)))
(defgeneric ccl::slot-makunbound-using-class (class object slotd) (:method ((class standard-class) object (slotd cons)) (%slot-makunbound object (slot-definition-name slotd))))
(handler-bind ((error (lambda (e) (let ((do-it (find-restart 'continue e))) (when do-it (invoke-restart do-it))))))
(defun slot-makunbound (object slot) (let* ((class (class-of object)) (slotd (or (find slot (class-instance-slots class) :key #'slot-definition-name) (slot-missing class object slot 'slot-makunbound)))) (ccl::slot-makunbound-using-class class object slotd))))
(export '(ccl::slot-makunbound-using-class) :ccl))
But, uhm, it does work. It's evil in a good way?
Personally, I'd go with slot-makunbound-using-class, and hack it into systems missing it (obviously, since I load the above code in my MCL init file). But if you find that to be in bad taste, I could do the md-slot-makunbound route. *I* think I have great taste, but not everyone always agrees; some people prefer to fake the funk.
Thomas F. Burdick wrote:
Kenny Tilton writes:
Thomas F. Burdick wrote:
So ... uh, which approach do you think is worse: using a c-slot-makunbound function that works for both normal and cell-mediated slots;
In this contrast between normal and cell-mediated, does normal mean a slot specified:
:cell nil
I still need to work on my Cells-related terminology; I mean this case. That way Cells-using programs could use the same makunbound for all objects, without worrying about where they came from.
OK. And you mean "all slots", as well.
Food for thought: slot-value is a back door. Not sure what that food means, tho, because for a while Cells /was/ mop-based and slot-value was not a back door. But that was slower and less portable. But those are practical concerns, not ideological. But consistency here might be a Good Thing if only for consistency's usual merits (hobgoblins notwithstanding).
More food: when slot-value was /not/ a backdoor, it was a nuisance to programming cells. I had to set a *backdoor* special to actually get the raw slot-value in Cell internals. ie, slot-value-using-class and its setter both had to (in all specializations) keep an eye out for *backdoor* access. OK, it is not the end of the world, but it may have been a Message From God, viz, that perhaps the transparency thing (making it seem as if CLOS does Cells) is a mistake: CLOS does /not/ do Cells. Cells by this thinking are not strictly an /extension/ of CLOS, but rather a distinct OO capability implemented thru CLOS. Less profoundly, should Cell internals ever care about slot bounditude, we'll be back into *backdoor* tricks.
Having to hack MCL folks' CLOSes becomes just another straw on the back at this point.
Thoughts?
kt
Kenny Tilton writes:
Food for thought: slot-value is a back door. Not sure what that food means, tho, because for a while Cells /was/ mop-based and slot-value was not a back door. But that was slower and less portable. But those are practical concerns, not ideological. But consistency here might be a Good Thing if only for consistency's usual merits (hobgoblins notwithstanding).
More food: when slot-value was /not/ a backdoor, it was a nuisance to programming cells. I had to set a *backdoor* special to actually get the raw slot-value in Cell internals. ie, slot-value-using-class and its setter both had to (in all specializations) keep an eye out for *backdoor* access. OK, it is not the end of the world, but it may have been a Message From God, viz, that perhaps the transparency thing (making it seem as if CLOS does Cells) is a mistake: CLOS does /not/ do Cells. Cells by this thinking are not strictly an /extension/ of CLOS, but rather a distinct OO capability implemented thru CLOS. Less profoundly, should Cell internals ever care about slot bounditude, we'll be back into *backdoor* tricks.
Hmmm ... maybe you're right. Exporting md-slot-value and making it bottom out to calling slot-value would make this argument stronger. Then, if you're using Cells, just use the md- variations for everything. If you're consuming object that you don't know too much about (are they Models?), you really don't want to be doing things like calling slot-value on them anyway.
Having to hack MCL folks' CLOSes becomes just another straw on the back at this point.
Thoughts?
Well, I do think it's more like a bug in MCL, since it's in line with the MOP they implement, but on reconsideration, the md- solution might be better. Since Cells really isn't integrated into CLOS, having this little feature integrated would be weird; the normal backdoor around it would be to write a method on s-m-u-c that circumvents Cells'. But since model classes aren't instances of a model-class metaclass, the backdoor would be to get an :around method around the Cells :around method ... okay, that might just be a message from Rakim (God MC).
--- /|_ .---------------------------------. ,' .\ / | It's been a long time, | ,--' _,' | I shouldn'ta left you | / / | without a dope rhyme to step to | ( -. | `---------------------------------' | ) | (`-. '--.) `. )----'