Что такое "не номинальный тип" в Swift?

Я вижу эту ошибку при попытке расширить неортодоксальные "типы" (Int, Int) или Any:

Не номинальный тип "Любой" не может быть расширен

Так что же делает тип не номинальным? Какая разница между не номинальным типом типа Any или (Int) и обычным номинальным типом типа Int?

Ответ 1

Это несколько догадка (править: неправильно, посмотрите на ответ Брент), но здесь идет:

Any - это протокол, а не фактический тип. Слово "Номинал" подразумевает именование (на основе корня слова).

Таким образом, вы не можете расширять Any потому что это протокол, а не фактический тип, и вы не можете расширять (Int, Int) потому что это просто буква кортежа, опять же не фактический тип, который вы могли бы указать по имени.


Обновить:

Разумеется, вы можете расширять протоколы. Any не является протоколом, он (шокер) не номинальный тип, который является чем-то другим. Прочтите ответ Брент; он сделал хорошую работу.

Ответ 2

В настоящее время принятый ответ неверен; Фактический ответ исходит из глубокой и эзотерической части системы типов Свифта.

Большинство типов в Swift являются номинальными типами - это "именованные" типы, объявленные в определенном модуле как часть пользовательского кода. Кажущиеся примитивными типы, такие как Int и Array, на самом деле являются структурами, определенными в модуле Swift, который автоматически импортируется в каждый исходный файл Swift. Даже Optional сам по себе является перечислением в модуле Swift, хотя компилятор добавляет немного магии, например, необязательное сцепление и принудительное развертывание.

Несколько исключений называются "неноминальными типами". Могут быть и другие, но основными из них являются:

  • Типы функций, такие как (Int) → String
  • Типы кортежей, такие как (Int, String)
  • Метатипы, такие как String.Type (тип выражения String.self)
  • CustomStringConvertible & Error, такие как CustomStringConvertible & Error

Существование заслуживает немного большего объяснения. Экзистенциал содержит значение, точный тип которого был удален, но известно, что оно соответствует определенному набору протоколов. Например:

// 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

Не номинальные типы все просто комбинируют другие типы каким-то специальным образом. (Их иногда называют "структурными типами", потому что они определяют некоторую общую структуру, объединяющую другие типы.) Их не нужно явно определять, и они не принадлежат какому-либо конкретному модулю - FooKit (Int, String) такой же, как BarKit (Int, String). Но все их поведение определяется языком - неноминальный тип не может иметь собственных методов или свойств, не может соответствовать протоколам и поэтому не может быть расширен.

Таким образом, вы не можете расширять Any потому что Any - это особая вещь, встроенная в язык, как тип функции или тип кортежа. Просто бывает, что имя написано буквами, а не пунктуацией.

(Так почему же вы можете расширить CustomStringConvertible? Потому что, в зависимости от контекста, CustomStringConvertible может означать протокол или экзистенциал, содержащий значение, соответствующее протоколу. Когда вы пишете extension CustomStringConvertible, вы расширяете протокол, но когда вы пишете let x: CustomStringConvertible, вы объявляете переменную, тип которой "экзистенциальный, содержащий значение, соответствующее протоколу". Это немного сбивает с толку, и некоторые из сопровождающих Swift фактически хотели бы потребовать, чтобы экзистенциал был записан как Any<CustomStringConvertible> Чтобы сделать это более понятным. Не очень вероятно, что это произойдет сейчас, когда они пытаются сохранить стабильность источника, хотя.)