Kenny Tilton writes:
Thomas F. Burdick wrote:
(defclass foo () ((a :accessor foo-a :type fixnum)))
and if you try to set an A slot to something other than a fixnum, you'll get a runtime error.
Good lord. Do they know CL is short for Common Lisp? Tell me they get huge runtime performance benefits from this sacrilege.
(What about "Steel Bank" says "fast and loose"? ;) Taking the CL type system seriously has a few benefits, one of which is can be being able to emit blazingly fast object code, but a big one is debugability. Unlike crummy languages, there's no reason you have to declare any types anywhere -- but if you *do*, you get runtime assurances. And with structures or objects, the big win is not scratching your head, wondering, "well how in the hell did NIL get *there*?". Instead, you can look at the stack exactly when NIL got there. But it's CL, so if you just want your cake, but don't want to eat it (you just like the way it looks perhaps?), you don't have to declare a single type anywhere.
Actually, there is a problem here. Currently Cell structures land in the slot during make-instance (when I had a MOP-based implementation I shunted Cells bound for a slot over to the .cells internals slot before they ever got to the slot, by placing an around method on (setf slot-value-using-class)). Possibly I can do the same with an around meth on shared-initialize, but till then SBCL will likely not be happy with a Cell in a slot typed numeric.
Well, right now, any value can actually go in a slot with a declared type: type checking only happens if you go through the accessor method. Cells-for-structures wouldn't work so hot, though.
Well, we'll put this in the hopper, but I propose we give higher priority to me documenting the system and getting Cello going.
Definately. I'm partly just bouncing ideas off of you, and if they seem workable, I'll make the changes myself, if I need them.
And I think I do want to make the resumed base value (what /do/ we call that?)
an at-rest value?
of an ephemeral a distinct attribute, so as not to shadow the semantics of an instance being initialized with an ephemeral slot initialized, say, (CV 2) with baseline value zero.
Okay, so the initial value would be just another value it takes on ephemerally, before going back to its resting state?
STYLE-WARNING: redefining SHARED-INITIALIZE :AFTER (FOO T) in DEFMETHOD
Hey, SBCL is creating an after method on shared-initialize?
Oops, that was TFB that did that, not SBCL.
5368709110
- (typep * 'fixnum)
NIL
Gee, what /is/ the type?
Too much? Actually, the answer is kind of amusing. CMUCL would tell you BIGNUM. SBCL, however, is a tiny bit more precise:
* (type-of 5368709110)
(INTEGER 536870912)
Did I mention they take the type system seriously?
In reverse order of simplicity, the best way to solve this would be to fix SBCL;
Have you asked the SBCL crowd whether they are set on slot-value being type-ignorant?
I'd assume just the opposite, that they consider it a bug (it seems like a gap in declarations-as-assertions), but I don't see it in the BUGS file, so maybe I'm wrong. I'll ask.
the second best way would be to either have Cells use :around methods, instead of redefining the primary methods, then invoke the proper next-method machinery
That scares me.in the abstract, tho I cannot point to any evil consequence, I just think one will arise in due course because it strikes me as a kluge.
Ooh, yeah, that might do bad things if you have one model that inherits from another (the next-method would be the superclass' :around method).
-- or, have Cells propagate the type declaration to its new method definitions.
That should be easy, and sounds Deeply Correct. I am already looking at the slot definition name to get reader and writer names, may as well grab the type if any. So what's the syntax for declaring a /return/ type? And the synatx on the setf side as well.
This seems like a good thing for Cells to do, because that way type declarations can more fully do their thing, whatever that means on the implementation you're using. For syntax:
* (macroexpand-1 '(defmodel foo () ((x :initform (cv 0) :accessor get-x :type fixnum) (10x :cell t :accessor get-10x :type fixnum))))
...
(PROGN (DEFMETHOD GET-X ((SELF FOO)) (CELLS::MD-SLOT-VALUE SELF 'X)) (DEFMETHOD (SETF GET-X) (NEW-VALUE (SELF FOO)) (SETF (CELLS::MD-SLOT-VALUE SELF 'X) (COERCE NEW-VALUE 'FIXNUM))) NIL)
(Holy crap, where'd that COERCE come from? That's not a good idea at all, you can lose object identity that way.)
With type declarations, that would be:
(progn (defmethod get-x ((self foo)) (declare (values fixnum)) ; <==== (cells::md-slot-value self 'x)) (defmethod (setf get-x) (new-value (self foo)) (declare (type fixnum new-value)) (setf (cells::md-slot-value self 'x) new-value)) nil)
Problem is, the type-checks don't go deeply enough into cells this way. If we propagage the type declarations for 10X in the same way, we get:
(progn (defmethod get-10x ((self foo)) (declare (values fixnum)) (cells::md-slot-value self '|10X|)) (defmethod (setf get-10x) (new-value (self foo)) (declare (type fixnum new-value)) (setf (cells::md-slot-value self '|10X|) new-value)) nil)
But when X is changed, and 10X is recalculated, this method doesn't get invoked.
[ Plus, SBCL can't check (VALUES ...) declarations yet, grrr. You'd have to do something ugly like:
(defmethod get-x ((self foo)) (let ((result (cells::md-slot-value self '10x))) (the fixnum result))) ]
Since we've got eager data propogation, it would be nice to get the type errors with the stack still intact. Obviously, though, this can wait for a bit (although I might make it one of my higher priorities once Cells is in CVS).
I hear a vote for early CVS. :)
Yeah, let's get your next .zip into CVS.
Hmm, :ephemeral-resumes seems like a weird name. Maybe :ephemeral-default ? Actually, I think it would be better to stick with the :cell :ephemeral :initform ( syntax
I managed to hit Send on accident here. That was supposed to end:
with the :cell :ephemeral :initform (cv ...) syntax, because then you could have the slot-value default to being unbound.
In RMS (an ISAM) one could specify that spaces or all zeros or whatever were the NULL_VALUE for an alternate key, meaning "if this is my value for this indexed field, just leave me the hell out of that index; i cannot be found that way". This was crucial where an indexed field was populated by only one or two percent weirdo records. Without NULL-VALUE, you get a million records with the same duplicate value, and an insertion could take 30 seconds as RMS read through a vast index tree looking for the place to insert. How is:
:cell :ephemeral :c-null-value 0 :initform (cv 0)
I just feel kinda bad making them type in the initform when at this point DEFMODEL has enough info to auto-supply that (and I do frequently neglect to code in (cv nil) on ephemerals. Should we add that nicety? Do we need a warning if they say :c-null-value and they do not say :ephemeral? The parallel error went unnoticed on an RMS file with consequences you would not believe.
The only problem I'd have with auto-supplying an initform is that you can't have it be unbound when at rest. Well, I guess you could have a cells:+unbound-value+, which says, "I don't want NIL, I want this unbound".
The nice thing about the suggested alternative
:ephemeral-resumes 0 (with :cell t by default,and of course a
better name)
is that it eliminates the redundant declaration
I guess the verbose option makes the common case pretty annoying, for the dubious value of allowing unbound as an option. Maybe :ephemeral-resting-value <at-rest-value>, plus a cells:+unbound+ ?