Subject: bug#77725: 31.0.50; Add support for types accepted by `cl-typep' to cl-generic?

From: Stefan Monnier <[email-masked]>
Date: Tue, 15 Apr 2025 15:17:13 -0400

>>> That would be great.  But I need some help here to correctly dispatch
>>> all functions in gtype to Emacs libraries: cl-macs, cl-generic ?
>> Not `cl-generic`, but yes `cl-macs.el` with maybe some parts in
>> `cl-preloaded.el` and/or `cl-lib.el` and/or `cl-extra.el`.
> Shouldn't the code related to method dispatching, at the end of gtype.el,
> go to cl-generic?

Technically it can go to various places, but ideally `cl-generic` just
provides the machinery whereas the actual generalizers would be
defined next to where we define the corresponding types.  In practice,
we have most of the `cl-generic-define-generalizer` in there,
admittedly, but that's because of overriding bootstrapping issues.

Since we're talking about generalizers defined for `cl-deftype` which is
not a core primitive but one defined in `cl-lib`, it makes sense to
define it elsewhere (i.e. in the `cl-lib` files), like we do for
EIEIO types.

>> I was thinking that the main(only?) difference between `cl-deftype` and
>> `gdeftype` is:
>> - the PARENTS argument, where the question is how to add a PARENTS
>>    argument.  Maybe we could use a (declare (parents ...))?
>
> Another possibility could be to have 2 separate definitions:
>
> - cl-deftype to define a data type, as currently.
>
> - cl-types-generalize (or something better) to declare a type previously
>   defined by cl-deftype usable to dispatch methods, with specified parents?
>
>   (cl-deftype my-type ()
>     "A user defined type."
>     ...)
>      (cl-types-generalize my-type) ;; No parents
>
>   (cl-deftype my-type1 ()
>     "Another user defined type."
>     ...)
>      (cl-types-generalize my-type1 my-type) ;; With parents

We could, but having them separate begs the question of how to match one
with the other: e.g. if we re-evaluate (cl-deftype my-type ...) should the
corresponding previous (cl-types-generalize ...) be "undone"?

It's somewhat philosophical, admittedly, but bringing them together
makes it much more clear what is the "right" behavior (regardless if we
end up implementing the right behavior: at least when we get
a bug-report, we know which change is an improvement).

>> - The ARGS: Clearly your `gtype-of` can invent which args to pass
>>    for a given value to match the resulting type, so `gtype-of` (and
>>    everything which relies on it, i.e. method dispatch) wouldn't be
>>    usable for types with a non-empty arglist.
>
> I am not sure I understand this point regarding ARGS.
> Is this related to the `cl-deftype' arguments which cannot be used to
> declare argument type of methods:

Exactly.  `cl-deftype` lets you define types such as `(list-of
integer)`, but your code is not able to figure out that a value is of
type `(list-of integer)` so you can't handle those kinds of types.
And that's OK: it's still better than what we currently have.

> But, below is possible instead:
>
> (defgtype u8 unsigned-byte ()
>   '(unsigned-byte 8))
>
> (cl-defmethod my-foo ((n u8))
>   (format "unsigned 8bits, %s - also %s"
>           n (cl-call-next-method)))

That's right.

We could try to later add support for

    (cl-defmethod my-foo ((n (unsigned-byte 8)))
      (format "unsigned, %s" n))

but ... one step at a time.

        Stefan