What is a 'non-nominal type' in Swift?

This is somewhat of a guess (edit: it's wrong, look at Brent's answer), but here goes:

Any is a protocol, not an actual type. The word "Nominal" implies naming (based on the root of the word).

So you can't extend Any because it's a protocol, not an actual type, and you can't extend (Int, Int) because that's just a tuple literal, again not an actual type that you could specify by name.


Update:

You can, of course, extend protocols. Any is not a protocol, it's (shocker) a non-nominal type which is something else. Read Brent's answer; he did a good job.


The currently accepted answer is incorrect; the actual answer comes from a deep and esoteric part of Swift's type system.

Most types in Swift are nominal types—they're "named" types declared in a particular module as part of user code. Seemingly primitive types like Int and Array are actually structs defined in the Swift module, which is automatically imported into every Swift source file. Even Optional itself is an enum in the Swift module, although the compiler adds a little bit of magic like optional chaining and force-unwrapping.

The few exceptions are called "non-nominal types". There may be others, but the main ones are:

  • Function types, like (Int) -> String
  • Tuple types, like (Int, String)
  • Metatypes, like String.Type (the type of the expression String.self)
  • Existentials, like CustomStringConvertible & Error

Existentials deserve a little more explanation. An existential contains a value whose exact type has been abstracted away, but which is known to conform to a certain set of protocols. For example:

// You can put anything that conforms to `CustomStringConvertible` in `x`
let x: CustomStringConvertible

// You can put anything that conforms to both `CustomStringConvertible` 
// and `Error` in `y`
let y: CustomStringConvertible & Error

// You can put anything in `z`; `Any` is an existential that doesn't
// require you to conform to any particular protocols
let z: Any

Non-nominal types all simply combine other types together in some ad-hoc way. (They're sometimes called "structural types" because they define some kind of general structure for other types to slot into.) They don't need to be explicitly defined, and they don't belong to any particular module—FooKit's (Int, String) is the same as BarKit's (Int, String). But all of their behavior is defined by the language—a non-nominal type can't have methods or properties of its own, can't conform to protocols, and therefore can't be extended.

So you can't extend Any because Any is a special thing that's built into the language, just like a function type or a tuple type. It just happens to have a name written in letters, not in punctuation.

(So why can you extend CustomStringConvertible? Because, depending on context, CustomStringConvertible might mean the protocol or it might mean an existential containing a value conforming to the protocol. When you write extension CustomStringConvertible, you're extending the protocol, but when you write let x: CustomStringConvertible, you're declaring a variable whose type is "existential containing a value conforming to the protocol". This is kind of confusing, and some of Swift's maintainers would actually like to require that the existential be written as Any<CustomStringConvertible> to make it more clear. Not terribly likely to happen now that they're trying to preserve source stability, though.)

Tags:

Swift