Что такое "не номинальный тип" в 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>
Чтобы сделать это более понятным. Не очень вероятно, что это произойдет сейчас, когда они пытаются сохранить стабильность источника, хотя.)