Hi Stefan, Sorry for this late answer. It took me a while to take it all in ;-) On 2025-04-11 18:26, Stefan Monnier wrote: >> Does this mean that for a `cl-deftype'-like generalizer to work >> correctly, it must be part of the "typof" generalizer? > > No, but it needs to be either always more precise or always less precise > than `type-of`. > > IOW, either: > > (eql (gtypeof-generalizer A) (gtypeof-generalizer B)) > IMPLIES ((eql (gtypeof-generalizer A) nil) > OR (eql (cl-type-of A) (cl-type-of B))) > > or the reverse. That's a current limitation in the cl-generic system. > > One way to solve this is to use a generalizer that returns either nil or > a pair containing both the `gtype-of` and the `cl-type-of`, so it's > trivially always more precise than `cl-type-of`. But beware: these > things are compared with `eql` so you need to "uniquify" them with > a sort of hashconsing scheme. I am not sure to understand this point. AFAICS, gtypes always take precedence over other types. So, provided I correctly understand your point, the gtype generalizer is more precise than `type-of`. For instance, if you define an icon type like this: (defgtype icon nil () "Builtin icon type." '(and symbol (satisfies iconp))) (gtype-of 'button) => icon (cl-type-of 'button) => symbol So you cannot have two methods with the same name, one to handle the symbol type, and one to handle the icon type. I agree, it is a limitation. Is this your point? I wonder if in practice it is really inconvenient, and worth the trouble of complicate more the implementation? Isn't it clearer to use different methods for icons and symbols? Aren't these types fundamentally different, and isn't the fact that an icon is a symbol just an implementation choice? > > BTW if you do > > (defgtype cons-car-foo nil () > "A cons with a `foo' car." > `(satisfies ,(lambda (x) (eq (car-safe x) 'foo)))) > > (defgtype cons-cdr-foo nil () > "A cons with a `foo' cdr." > `(satisfies ,(lambda (x) (eq (cdr-safe x) 'foo)))) > > what's the `(gtype-of '(foo . foo))` ? Regarding this point, perhaps it would be possible to use an heuristic like: "in case of conflict, the last defined type is chosen as the most specific"? I implemented this idea in gtype (attached) to compute the gtype precedence list, by using a topologic sort with a tie-breaker to decide which gtype will be the most specific between two candidates that cannot be separated otherwise. It should be easy to implement different heuristics by providing different tie-breakers. With your example, if you define `cons-cdr-foo' after `cons-car-foo': (gtype-of '(foo . foo)) => cons-cdr-foo And, if you define `cons-car-foo' after `cons-cdr-foo': (gtype-of '(foo . foo)) => cons-car-foo To avoid inconsistent method dispatching, the order of gtypes does not change if you eval again the definition of an existing gtype. But the order will change if you first remove the gtype, then define it again. >> Or am I missing something? --- Sorry, I only started studying >> cl-generic relatively recently, and I probably don't understand >> all its intricacies ;-) > > Yeah, it's unsatisfactorily intricate, indeed. It's designed first and > foremost to keep the dispatch "simple and reasonable fast", at the cost > of making `cl-generic-define-generalizer` a very sharp tool. 🙂 > > I have recently been thinking about how to make it more reliable (which > would also make it more flexible/powerful, allowing the definition of > both `and` and `or` specializers). I have some vague idea, but there's > no code at all yet, and it might come with some non-trivial tradeoffs > (e.g. preloading the byte-compiler). I can't wait to see this :-) Thanks for your patience!