52

I see this error when trying to extend unorthodox 'types' like (Int, Int), or Any:

Non-nominal type 'Any' cannot be extended

So what makes a type non-nominal? What's the difference between a non-nominal type like Any or (Int) and a regular nominal type like Int?

LinusGeffarth
  • 27,197
  • 29
  • 120
  • 174
Joseph Mark
  • 9,298
  • 4
  • 29
  • 31
  • I was getting this error by using a typealiase between two protocols and trying to extend the combined protocol... – mfaani Nov 30 '18 at 18:44

3 Answers3

121

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

Becca Royal-Gordon
  • 17,541
  • 7
  • 56
  • 91
  • 2
    `Void` is another non-nominal type – Islam Jul 01 '18 at 13:06
  • 13
    @IslamQ.: To be more precise, Void is a [`typealias`](https://github.com/apple/swift/blob/master/stdlib/public/core/Policy.swift#L53) for an empty tuple. – Dennis Vennink Jul 11 '18 at 12:23
  • 1
    Why are types like `CustomStringConvertible & Error` not called Determinatives? Existential should indicate a kind of feature required to exist. In terms of programming code execution, it is invalid. – lyzkov May 31 '20 at 17:52
  • 1
    Some of the compiler's terminology for protocols, like "existential" and "witness", comes from analogies with mathematical predicate logic. The compiler developers understand that these terms are not very user-friendly and try to keep them out of diagnostics and documentation, but unfortunately nobody has settled on a better user-facing term for "existential", so that one has leaked out a bit. – Becca Royal-Gordon Oct 09 '20 at 23:37
3

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.

Islam
  • 3,654
  • 3
  • 30
  • 40
Jiaaro
  • 74,485
  • 42
  • 169
  • 190
  • It's different to a protocol though. `AnyObject` is a protocol and extending that gives the error: `Protocol AnyObject cannot be extended`. So `Any` is something different – Joseph Mark Jun 10 '14 at 01:33
  • 4
    `Any` is a `typealias` of an empty protocol it's full definition is `typealias Any = protocol<>`. You can combine protocols that way: `typealias MyPrintableSequance = protocol` and use it as if it was a protocol – Jiaaro Jun 10 '14 at 01:36
  • I didn't know that protocols could be combined, that's good to know. how did you get the full definition of Any? – Joseph Mark Jun 10 '14 at 01:47
  • 2
    @sjeohp I typed `var x: Any` in the playground, then ` - clicked` on "Any" - that file is just headers, but in the case of a typealias, the header is the whole thing – Jiaaro Jun 10 '14 at 02:02
  • but doesn't Swift have protocol extensions as stated [here](https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID521)? I'm in Swift 3 and still get the same error when attempting to extend `Any`. I know it's a terrible idea to extend `Any` but out of curiosity I'm trying to do it – mfaani Dec 19 '16 at 19:33
  • You absolutely can extend functionality of protocols. – Cody Winton Mar 10 '18 at 16:32
0

[Swift types]

Non-nominal type 'Pair' (aka '(key: String, value: String)') cannot be extended

Swift operates by two types:

  1. named type or nominal type or type with name like:
    • class
    • structure
    • enumeration
    • protocol
  2. compound type or non-nominal type or type without name like:
    • function
    • tuple

This error you get when extends compound type. For example

public typealias Pair = (key: String, value: String)

public extension Pair { //compile time error

}

As a variant to use extends functionality you can use named type instead

yoAlex5
  • 29,217
  • 8
  • 193
  • 205