Introduce existential `any` - avoiding inconsistency around `Any` and `AnyObject`

Splitting this off from SE-0335: Introduce existential `any` to keep original thread focused on the review.

Proposal SE-0335 " Introduce existential any " in order to avoid redundancy in spelling of any Any suggests special treatment of protocols Any and AnyObject allowing them to be used as existential types without the any keyword. This introduces inconsistency into the language and makes it unclear if Any and AnyObject are protocols or existentials.

I'd like to investigate if it is possible to avoid this redundancy, while maintaining consistent behaviour for all protocols in the language.

If all protocols need any to be used as type, and Any does not need it, then Any must be an existential type. But according to the discussion in SE-0335: Introduce existential `any` there is demand for Any as protocol as well.

One of the ideas suggested in the original thread was to name it Value, with some concerns that this identifier will frequently shadowed, and require disambiguation as Swift.Value.

Any as protocol is rarely used, and while it needs to exist, its spelling does not need to be the most convenient one. This allows to consider some exotic alternatives for naming the null protocol - like resurrecting non-nominal spelling protocol <> or using non-ASCII identifier like π•Œ (for universal set).

But existential for arbitrary metatype is used pretty often and any (protocol <>).Type does not look very readable. So this approach would need to be complimented with AnyType for convenience.

Also, I've been thinking about AnyType, and realised that if we cannot spell Any as protocol, then AnyType is still not enough - we would also need AnyTypeType, AnyTypeTypeType, and so on. Looks like having Type as a protocol would work then - you could spell any Type, any Type.Type, etc.

If we can spell Any as protocol, but spelling is ugly, then technically we can use AnyType as a convenience for any (protocol <>).Type, while any (protocol <>).Type.Type would need to be spelled as is. This creates new inconsistency. It will affect only a tiny fraction of users, but it is still an inconsistency. So probably any Type is still a better idea than AnyType.

But there is a difference between "type being a metatype" and "type conforming to protocol Type". If we make Type a true protocol and make all metatypes conform to it, then "type being a metatype" implies that "type conforms to Type", but the reverse is not necessarily true. Regular protocols can be adopted by somethings else. So for the idea of Type to work, the Type protocol would have to be special, to be more of a notation for denoting metatypes, then an actual protocol. Making Type a protocol alias for Value.Type/(protocol <>).Type would express well what Type is.

@hborla mentioned that this was not planned to work. Is this a good enough reason to reconsider and at least enable making aliases to P.Type?

// in stdlib:
typealias Type = Value.Type

// in user code
struct S: Type {} // error: types cannot explicit declare conformance to metatype constraint
func f<T: Type>(_ x: T) // error: metatype constraint cannot be used as generic constraint

To summarise the options:

Based on Value:

Old Canonical Typealias for Any Typealias for Any and AnyType Typealias for Any and Type
Any as type any Value Any Any Any
Any as constraint Value Value Value Value
Any.Type any Value.Type any Value.Type AnyType any Type
Any.Protocol (any Value).Type Any.Type Any.Type Any.Type
Any.Type.Type any Value.Type.Type any Value.Type.Type any Value.Type.Type any Type.Type
Any.Type.Protocol (any Value.Type).Type (any Value.Type).Type AnyType.Type (any Type).Type

Based on protocol <>:

Old Canonical Typealias for Any Typealias for Any and AnyType Typealias for Any and Type
Any as type any protocol <> Any Any Any
Any as constraint protocol <> protocol <> protocol <> protocol <>
Any.Type any (protocol <>).Type any (protocol <>).Type AnyType any Type
Any.Protocol (any protocol <>).Type Any.Type Any.Type Any.Type
Any.Type.Type any (protocol <>).Type.Type any any (protocol <>).Type.Type any any (protocol <>).Type.Type any Type.Type
Any.Type.Protocol (any (protocol <>).Type).Type (any (protocol <>).Type).Type AnyType.Type (any Type).Type

I don't see how this reflects the Swift naming conventions. The convention AnyObject has been used several times by multiple different entities:

  • Swift Standard Library: AnyHashable, AnyIterator, AnyKeyPath
  • SwiftUI: AnyView
  • Swift for TensorFlow: AnyTensor
  • MetalFFT (my own work): AnyFFTBuffer

This request is similar to the usage of some. There is no precedent to SomeKeyPath, SomeView, or SomeTensor–or any conceivable way to compare Any to some (besides relating to type erasure).

I believe the best solution for this is to change the names of Any and AnyObject, removing the word Any. I think the best replacement names are Instance and Object, since a class is to the word "object" as any Swift type is to "instance". It would also be more consistent with the naming of the Actor protocol.

The name Value (which seems to be a popular choice for Any's replacement) isn't an accurate description of the protocol. Non-value types (including reference types) would conform to Value, which could be confusing.

AnyObject is inconsistent with this naming convention. AnyHashable, AnyIterator, AnyCollection, AnyKeyPath, AnyView, and other similar types are all type-erasing wrappers. AnyObject is not a wrapper; it's the protocol that all classes implicitly conform to.

I'd also like to point out that Any does not follow the Swift API Design Guidelines.

  • Protocols that describe what something is should read as nouns (e.g. Collection).
  • Protocols that describe a capability should be named using the suffixes able , ible , or ing (e.g. Equatable , ProgressReporting).

Any is a determiner, not a noun, and doesn't describe a capability of conforming types. Instance, on the other hand, is a noun that accurately describes what a conforming type is.

Terms of Service

Privacy Policy

Cookie Policy