Introduce a way to annotate the class Any itself

Abstract

Add a way to annotate the class Any itself.

I addressed this here:

And before that in this StackOverflow post:

Motivation

Currently there is no way in the type system to annotate a variable that holds the class Any. Any is a special form in the type system and therefore it behaves in a special way for type checkers. From the docs:

  • Every type is compatible with Any.
  • Any is compatible with every type.

Therefore, when type[Any] is specified, it doesn’t mean the class Any itself, but any object that is a class.
This leaves us with the exceptional case that the class Any itself cannot be perfectly typed if stored in a variable.

This is not a very common problem, but when needed it is impossible to solve elegantly in the current type system.

Subclasses of Any are not affected, nor instances of them. Any itself cannot be instantiated, therefore typing instances of Any is not a required functionality either.

Specification

There are 3 possible ways to implement this:

  1. Make _AnyMeta public and usable from outside in type annotations
  2. Add an alias for _AnyMeta called AnyType, similar to EnumMeta and EnumType
  3. Allow the use of Literal[Any] to mean the class Any

Backwards Compatibility

No breaking changes.

Security Implications

None.

How to Teach This

When learning how to use Any, the programmer quickly learns that it has special meanings. When they need to type the class Any itself, they might themselves realize that in the typical way, it isn’t possible. They will then learn about the introduced feature.

Reference Implementation

Depending on the implementation either:

  1. Make the _AnyMeta class public and rename/alias it
  2. Create an alias to _AnyMeta
  3. Make type checkers not error when Any is used inside of Literal and give it the special meaning explained above

Rejected Ideas

In the linked topic, another alternative was suggested, but it is worse and not acceptable:
The idea was to (ab)use the (likely to be added) 3.15 feature TypeForm to express the class Any. TypeForm[Any] would annotate the class Any, while TypeForm would annotate any type.
This is not the preferred solution, because TypeForm should act as an extension of type to annotate types instead of just classes. Because type and type[Any] mean the same thing, it would be confusing to have TypeForm and TypeForm[Any] mean something different.

Open Issues

Decide which solution of the 3 proposed is the best. Other ideas are welcomed of course.

Acknowledgements

All people from the StackOverflow post and the TypeForm topic who repsonded and gave their ideas and opinions.

Footnotes

None.

Copyright

This document is placed in the public domain or under the
CC0-1.0-Universal license, whichever is more permissive.

Edits: Fix bad grammar, improve the title, replace placeholder of “reference implementation” and more minor fixes…

Moved to the Typing category.

Please don’t post long screeds in PEP form for a draft idea. It is more compelling to succinctly set out the core idea in a few paragraphs. More people are likely to engage and respond if the first post is shorter.

A

3 Likes

I suppose you mean _AnyMeta here?!

Yes I fixed it.

Do you have any examples of use cases?

That might help determine which of your 3 proposed solutions would be most appropriate.

In your SO post from June you admit:

Technically I could use some kind of sentinel object instead of the Any type

I think you should use a sentinel.

1 Like

The special forms in typing aren’t themselves types in the same way that most classes are.[1] With some exceptions for things like aliased values for abc collections (things like Typing.List) and for forms that are equivalent to Syntax (things like typing.Union), These are special cases to represent typing concepts that you can’t actually express without them.

Waiting for and then using TypeForm seems most appropriate for this.


  1. some of them, including Any, are still also used to represent type system types, but you’re interested in if the annotation is the type form representing Any, which is semantically different from the meaning of type[Any] ↩︎

But in my post I pointed out exactly why using TypeForm for this is inappropriate. For any other special case, your argument would be valid, but for Any an alternative must be found.

Or do you think making TypeForm[Any] not be the same as TypeForm, while type[Any] is the same as type, is a good idea?

I think every object should be typeable in the type system and Any feels more natural to use than a sentinel in many cases. In my case, I would have preferred using Any if it was possible instead of using that workaround.

Fair enough, but that’s exactly it - this is nothing more than a simple matter of preference.

I know sometimes we write function signatures and overloads that take a type-type parameter. (but even there, why isn’t None good enough to indicate “No Type info given”).

My preference is for a sentinel, because if I’m reading your code and see that pattern, especially if there’s something called “sentinel”, I know exactly what’s going on right away, and I gain insight into what the problem is and how the code solves it.

If reading code and I see Any used as a normal non-annotation value (with a type annotation no less) then I start to think "Why the heck did they do that, instead of just using None or a sentinel? Is there some special feature of Any that I don’t know about? ".

I’m just saying, for me, it’s not worth it, simply to pick the name Any for a do-nothing value.

2 Likes