[closer-devel] Introducing new "type" classes

Marco Antoniotti marcoxa at cs.nyu.edu
Sat Feb 16 17:40:51 UTC 2008


Hi Pascal


On Feb 16, 2008, at 17:17 , Pascal Costanza wrote:

> Hey,
>
> On 15 Feb 2008, at 16:58, Marco Antoniotti wrote:
>
>> Hi
>>
>> this is a little OT wrt Closer, but this is probably the venue  
>> where most of the expertise meets.
>>
>> In CLOS you have classes for INTEGER, REAL etc etc.  You can then  
>> write methods like
>>
>> 	(defmethod foo ((i integer)) ...)
>>
>> Now, we cannot write methods like
>>
>> 	(defmethod foo ((i (integer 1 42))) ...)
>>
>> and this may be ok.
>
> That's actually relatively easy: Just write a macro 'my-defmethod  
> that expands into a number of methods with eql specializers (42  
> different methods in this case). ;)

That is another answer.  I was asking for the question. :)


>
>> However, i am playing around with things like
>>
>> 	(deftype positive-integer () '(integer 1 *))
>>
>> is there any magic I can conjure to (semi) portably having a class  
>> named POSITIVE-INTEGER that actually corresponds to the type  
>> definition?
>
> The specializers are used to determine the applicability of  
> methods. The result of compute-applicable-methods must be a list of  
> applicable method, sorted according to their specificity (!). This  
> is where the problem is: As soon as you try to use more general  
> types than classes and eql specializers, you get into trouble with  
> sorting. For example, assume you have two methods specilaized on  
> the types (satisfies oddp) and (satisfies primep), and both  
> trigger, which one gets executed first?
>
> The general problem here is that you need to analyze the  
> specificity statically, without running any of the predicates. This  
> is generally not possible. (Consider (satisfies haltp). ;)
>
> If you allow for subsets, you get similar problems, although  
> superficially speaking, they seem more tractable. But consider you  
> have two methods specialized on the types (integer 0 *) and  
> (integer 1 10) - which one is more specific? Hard to tell.

I have not read the papers by Christophe and Jim you mention below  
(let me look for them), but I guess everything boils down to the  
definition of "specificity".   For classes and EQL specializers you  
have a notion, but why should this not be hammered into a subset  
relationship, ANSI 4.2.2 "Type Relationships" notwithstanding?

Given that SATIFIES is a no-no in any case when wanting to do  
something useful with types (at least w.r.t. compilation: Python does  
not deal with SATISFIES and, last I checked, AND types are dealt with  
in a limited way) and that therefore we can readily hide it under the  
carpet for the time being, we have that (integer 1 10) is a subset of  
(integer 0 *), thus I would consider it more specific.  The issues  
really start when you deal with intersections, e.g.

(defmethod foo ((i (integer 0 100))) ...)
(defmethod foo ((j (integer 32 1024))) ...)

(foo 42)

But even in this case you could raise an error at (re)definition time  
because you are defining a method which would require a runtime  
choice (in the above example at the definition time of the second FOO  
method).

Am I making sense or is there a hole somewhere?  Are there type  
subset tests that could not be made at (re)definition time?

> Christophe Rhodes has worked on the technical issues in the CLOS  
> MOP to be able to plug in new specializers, and implemented this  
> for SBCL. However, this covers 'only' the technical aspects of the  
> MOP, not the semantic issues sketched above. Jim Newton has come up  
> with an idea how to turn the decision which methods are more  
> specific into a property of the respective generic function. This  
> doesn't really solve the issue, you still need ad-hoc solutions,  
> but at least they can look different for different generic  
> functions, which I think is an improvement. (Both Christophe and  
> Jim have written papers about their ideas, presented at last year's  
> European Lisp Workshop.)
>
> For pragmatic purposes, a compromise could be to have a two-step  
> process: Base the applicability on classes and eql-specializers,  
> and then use a filter to get rid of methods you actually don't  
> want. A method definition could then look like this:
>
> (defmethod* foo ((x integer))
>   (:filter (x (integer 1 10)))
>   ...)
>
> Maybe this could be added as new qualifiers and the filtering could  
> then be part of a user-defined method combination. This would then  
> be portable.
>
> However, I don't know how much this would be an improvement over this:
>
> (defmethod foo ((x integer))
>   (unless (typep x '(integer 1 10))
>     (return-from foo (call-next-method)))
>   ...)

I do not like either cases.  They break down the modularity of  
defmethod and do amount to something like

(defmethod (x y .. (i integer) .. w)
      (filter (i integer)
         ((integer 0 42) 42)
         ((integer -42 0) -42)
         (integer 0)))

where FILTER is a macro (macrolet? automagicallymacroizedmacro? that  
works kind of TYPECASE and ensures that all the types in the clauses  
heads are SUBTYPEP of INTEGER.   This gives you full control, and it  
is readily built, but it does break the modularity and the magic of  
DEFMETHOD.

> [These thoughts are a bit random, sorry for that. But they are a  
> rough summary of my views on this issue I have developed so far...]

No no.  This is all good and helpful.


Cheers

Marco



--
Marco Antoniotti





More information about the closer-devel mailing list