[Openmcl-devel] OpenMCL MOP Issue

Gary Byers gb at clozure.com
Mon Feb 26 17:40:31 PST 2007



On Mon, 26 Feb 2007, Alexander Repenning wrote:

>
> On Feb 18, 2007, at 9:08 AM, Gary Byers wrote:
>
>> I'm not sure if there are good tutorials on the CL type system available
>> online or elsewhere.  (The presentation in CLtL[1|2] is probably less dry
>> than that in CLHS.)
>> 
>> Consider the case (like your original example) of:
>> 
>> (defclass foo ()
>> ((a :type boolean)))
>> 
>> There are several different but equivalent ways of referring to "the
>> built-in type that contains T and NIL, exactly".
>> 
>> BOOLEAN is one way of referring to that type.
>> 
>> (MEMBER T NIL) is another way.
>> 
>> (OR (EQL T) (EQL NIL)) is yet another way.
>> 
>> (AND BOOLEAN) and (AND (MEMBER T NIL)) are still other ways.
>> 
>> Since classes are type-specifiers, the builtin class BOOLEAN (the value
>> returned by (FIND-CLASS 'BOOLEAN)) can also be used to refer to this type.
>
> I don't understand. There is no built in class boolean.

My mistake: I was trying to point out that class objects are valid
type specifiers, but forgot that there's no built-in BOOLEAN class.


>
>> 
>> In general, the type of a slot in an EFFECTIVE-SLOT-DEFINITION is the
>> intersection of the type specified in the DIRECT-SLOT-DEFINITION (or
>> the DEFCLASS form) and the types of any inherited slots with the same
>> name.  (See the AMOP entry for COMPUTE-EFFECTIVE-SLOT-DEFINITION and
>> CLHS 7.5.3) That intersection is a -computed- value; in the case where 
>> there
>> are no inherited slot definitions, it's equivalent to (AND <type>) , or
>> (AND BOOLEAN) in the original example.  As we saw above, (AND BOOLEAN)
>> is equivalent to BOOLEAN, so BOOLEAN would be a correct result. (AND 
>> BOOLEAN) is also equivalent to (MEMBER T NIL), so that's also
>> a correct result.
>> 
>> I don't think that there's any reason to think that any correct result
>> is more correct than any other correct result, or that the type of
>> the effective slot has to be specified in exactly the same way that
>> it was specified in the DEFCLASS form/direct slot definition; there's
>> neither any reason for COMPUTE-EFFECTIVE-SLOT-DEFINITION to to do this nor 
>> any reason not to in the case where there are no inherited slots.
>
>
>
> Yes, both result would be equally correct. The main problem is perhaps not 
> one of correctness but of convention. Every Lisp we have tested so far (MCL, 
> Allegro, LispWorks) will return the symbol specified in :type of the DEFCLASS 
> if there are no or type compatible inherited slot definitions. The MOP 
> dictionary seems to hint at this as well 
> http://www.lisp.org/mop/dictionary.html by talking about type specifier 
> names.

Other implementations may not pay any attention to the :type option
in a slot specifier (this was true as of a few years ago; I don't know
if it still is.)  This is perfectly legal: the implementation isn't
required to check to see if a slot's type constraints are violoated.
OpenMCL does try to perform this check.  (Newer versions of MCL
did as well, but neglected to intersect the specified slot type with
any inherited slot types: MCL simply copied the slot-definition-type
from the direct-slot-definition to the effective-slot-definition, even
in cases where there are inherited slots. in some cases, it could
wind up enforcing an incorrect slot type constraint.  That was my
bug.)


>
> Especially given that both results are equally correct but one seems to be 
> more desirable I would hope you could be talked into this "optimization" It

One seems to be "more desirable" for what reason ?  For enforcing type
constraints, or for some other reason ?  (The value returned by
SLOT-DEFINITION-TYPE of an effective-slot-definition might or might
not be used to enforce type constraints; if it -is- used, I think that
it's "more desirable" that it be in a useful/canonical form.  (I think
that OpenMCL usually tries to use an even more useful/ canonical way
of denoting the type to do slot type-checking, but I'd certainly want
to be -able- to use the type-specifier in the SLOT-DEFINITION-TYPE if
there was reason to do do.   I would not think that it's in anyone's
interest to require the implementation to ensure that the computed
slot type specifier was EQ to the provided one.

> would be in synch with all the other Lisps and make things much simpler. It 
> is an optimization in the sense that, as you point out, there would be no way 
> to return a type specifier name in the case of type modifying superclass type 
> specifiers. However, we find that in about 99% of our applications that is 
> not an issue.

If all of the other lisps jumped off a cliff ...

Seriously, the reason why BOOLEAN comes out as the equivalent (MEMBER T NIL)
just has to do with how the type specifier is canonicalized when computing
the intersection, and the case where the there are no inherited slots
could be special-cased (and the direct slot's type-specifier used verbatim
in that case.)

If some other implementation decided to use the effective slot's
type-specifier for type-checking, shouldn't it be allowed to turn
something like:

(or (integer #.most-negative-fixnum -1) (integer 0 #.most-positive-fixnum))

into

FIXNUM ?  (I'd personally hope that it did ...)

>
> If OpenMCL does not return a type specifier name then CLOS based dispatching 
> simply becomes much trickier. Instead of being able to do:
>
> (defmethod READ-TYPED-ATTRIBUTE-VALUE ((Value string) (Type (eql 'integer)))
> (parse-integer Value))
>
> we would have to do:
>
> #+:OpenMCL
> (defmethod READ-TYPED-ATTRIBUTE-VALUE ((Value string) (Type t))
> (cond
>  ((subtypep Type 'boolean)  <do the boolean>)
>  ((subtypep Type 'integer) (parse-integer Value))
>  ...))
>

You're getting the TYPE argument from an EFFECTIVE-SLOT-DEFINITION,
and it's a computed value in OpenMCL but apparently just copied
verbatim from some DIRECT-SLOT-DEFINITION in other implementations ?

One portable way of finding the most specific SLOT-DEFINITION-TYPE
- as specified in a DEFCLASS somewhere and without having gone
through any transformations to make it more  ... desirable as an
EFFECTIVE-SLOT-DEFINITION's type - would be to look for the
matching slot in the CLASS-DIRECT-SLOTS of the class and its
superclasses.

(defun find-declared-slot-type (name class)
   (dolist (c (class-precedence-list class) (error "slot named ~s not found in class ~s" name class))
     (let* ((dslotd (find name (class-direct-slots c) :key #'slot-definition-name)))
       (when dslotd
         (return (slot-definition-type dslotd))))))

instead of

(defun find-effective-slot-type (name class)
   (let* ((eslotd (find name (class-slots class) :key #'slot-definition-name)))
     (unless eslotd (error ...))
     (slot-definition-type eslotd)))

Unless I'm missing something, FIND-DECLARED-SLOT-TYPE (a) portable (b)
not grossly less efficient than FIND-EFFECTIVE-SLOT-TYPE and (c) not
sensitive to anything that the implementation may or may not do to
promote/canonicalize/simplify/intersect effective slot types.

(For people who may not remember: a DIRECT-SLOT-DEFINITION is an
object that represents the attributes of a slot defined in a class
definition; an EFFECTIVE-SLOT-DEFINITION represents the attributes
of slot that's present (either directly or via inheritance) in an
instance of a class.  My argument is that an implementation should
be free to use a computed type-specifier to denote the type of an
effective slot; many implementations don't do this, but user code
shouldn't count on whether they do or not.)



More information about the Openmcl-devel mailing list