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

From: David Ponce <[email-masked]>
Date: Wed, 16 Apr 2025 17:17:36 +0200

On 2025-04-15 21:17, Stefan Monnier wrote:
>>>> 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.

OK.

> 
>>> 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).

I agree.

> 
>>> - 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.

OK.

> 
> 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.

Sure.  Even better could be to support the full type-specifier syntax
;-)

Please find attached a first attempt at a cl-types.el to be merged in
cl-lib.  Please feel free to propose new names if mine are not good.

For now, the new `cl-deftype' is named `cl-deftype2' for testing
without impacting the existing environment.

I tried to consistent at using the `cl-type-' prefix (or `cl--type-'
for internals).  But some names are very close to already existing
ones, like 'cl-type-p' vs. `cl-typep'.  There is also a collision with
the name `cl-type-of', already used for a builtin function, so I used
the name `cl-types-of' which also better represent that this function
returns a list of types.

The new code is also better at handling errors.  It should detect most
of the possible errors, and will restore the current environnement on
error.  I also tried to improve cl-types-of.  Please read my comments
in the code.

WDYT?

Thanks!
David