Subject: bug#69739: closed (Re: bug#69739: 30.0.50; `type-of` is not precise enough)

From: [email-masked] (GNU bug Tracking System)
Date: Mon, 18 Mar 2024 13:35:02 +0000

Your bug report

#69739: 30.0.50; `type-of` is not precise enough

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
69739: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=69739
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#69739: 30.0.50; `type-of` is not precise enough
From: Stefan Monnier <[email-masked]>
To: Eli Zaretskii <[email-masked]>
Cc: [email-masked], [email-masked], [email-masked]
Date: Mon, 18 Mar 2024 09:33:58 -0400
List-Id: <debbugs-submit.debbugs.gnu.org>

>> +@example
>> +(object-type 1)
>> +     @result{} fixnum
>> +@group
>> +(object-type 'nil)
>> +     @result{} null
>> +(object-type (record 'foo))
>> +     @result{} foo
>
> "object-type"?

Oops!  thanks.

>>  DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0,
>>         doc: /* Return a symbol representing the type of OBJECT.
>>  The symbol returned names the object's basic type;
>> -for example, (type-of 1) returns `integer'.  */)
>> +for example, (type-of 1) returns `integer'.
>> +Contrary to `cl-type-of' the returned type is not always the most
>                           ^^
> I think we want a comma there.
>> +DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0,
>> +       doc: /* Return a symbol representing the type of OBJECT.
>> +The symbol returned names the most specific possible type of the object.
>    ^^^^^^^^^^^^^^^^^^^
> I think "The returned symbol" is better here, as it prevents a
> possible confusion (whether "returned" alludes to "symbol" or to
> "names").

Agreed.

>> +for example, (object-type nil) returns `null'.
>                  ^^^^^^^^^^^
> "object-type"?

As you can see I had used `object-type` instead of `cl-type-of` in some
prior version of the code :-)

>>  (defsubst subr-primitive-p (object)
>> -  "Return t if OBJECT is a built-in primitive function."
>> +  "Return t if OBJECT is a built-in primitive written in C."
>>    (declare (side-effect-free error-free))
>>    (and (subrp object)
>>         (not (subr-native-elisp-p object))))
>>  
>> +(defsubst primitive-function-p (object)
>> +  "Return t if OBJECT is a built-in primitive function."
>> +  (declare (side-effect-free error-free))
>> +  (and (subrp object)
>> +       (not (or (subr-native-elisp-p object)
>> +                (eq (cdr (subr-arity object)) 'unevalled)))))
>
> Should these doc strings mention the special case of special form,
> which each one of them treats differently?

OK.

Pushed, thanks,

        Stefan

Subject: 30.0.50; `type-of` is not precise enough
From: Stefan Monnier <[email-masked]>
To: [email-masked]
Date: Mon, 11 Mar 2024 19:19:40 -0400
List-Id: <debbugs-submit.debbugs.gnu.org>

Package: Emacs
Version: 30.0.50

`type-of` is supposed to return "the" type of its argument.  Given that
ELisp has a notion of subtyping, "the" type is expected to mean "the
most precise type".  This is used in `cl-generic` to decide which method
to apply, so it's important that it returns precise information.

Currently, there `type-of` fails to return precise enough information in
a few cases:

- When the argument is nil, it returns `symbol`.  The problem here is
  that `symbol` is not a subtype of `list`, whereas nil is a list.

- When the argument is a special form, a C primitive, or
  a native-compiled function it returns `subr`.  Currently our
  type hierarchy says that `subr` is a subtype of `compiled-function`
  (and hence of `function`), but a special form is *not* a function
  (it fails the `functionp` test and can't be `funcall`ed).

Currently `cl-generic` works around the first point above by using
(if FOO (type-of FOO) 'null) instead of calling `type-of` directly.

Suggestion:

    I suggest we change `type-of` to return `null` for `nil`,
    `special-form` for subrs that are special forms, `subr-primitive`
    for C primitives, and `subr-native-elisp` for native-compiled subrs.

There are a few other cases where we could improve the precision, tho
they are less important because they don't cause problems w.r.t
subtyping like the above does.

Further improvements could include:

- Return `boolean` for `t`.  This would be nice otherwise (with the
  above suggestion) `cl-generic` can dispatch on "nil is a boolean"
  but not on "t is a boolean".
- Return `keyword` for symbols that are keywords.
- Return `fixnum` or `bignum` rather than just `integer`.
  Probably not worth the trouble.
- We could go crazy and return `keyword-with-pos` for `symbols-with-pos`
  that are keywords.

Of these further improvements, only the first (return `boolean` for `t`)
seems worth the trouble.

Still, any change as suggested here would be an incompatible change, so
there's risk it'll break some code out there (`type-of` is not used very
often, but it *is* used).  Another option is to introduce a new function
which does the same as `type-of` but with changes like the ones above.
(we could even decide to give it a `cl-generic-` prefix to discourage
its use elsewhere so we can be more free to change its return value in
the future).

        Stefan