В теории типа , типа пересечения могут быть отнесены к значениям , которые могут быть назначены как тип и тип . Это значение может быть дан тип пересечения в качестве системы типа пересечения . [1]
Обычно, если диапазоны значений двух типов перекрываются, то значению, принадлежащему пересечению двух диапазонов, может быть присвоен тип пересечения этих двух типов. Такое значение можно безопасно передавать в качестве аргумента функциям, ожидающим любого из двух типов. Например, в Java классовые реализует оба иBoolean
Serializable
Comparable
интерфейсы. Следовательно, объект типа Boolean
может быть безопасно передан функциям, ожидающим аргумент типа, Serializable
и функциям, ожидающим аргумента типа Comparable
.
Типы пересечения - это составные типы данных . Подобно типам продуктов , они используются для присвоения объекту нескольких типов. Однако типы продуктов назначаются кортежам , поэтому каждому элементу кортежа назначается определенный компонент типа продукта. Для сравнения, лежащие в основе объекты типов пересечения не обязательно являются составными. Ограниченная форма типов пересечения - это типы уточнения .
Типы пересечений полезны для описания перегруженных функций . [2] Например, если это тип функции, принимающей число в качестве аргумента и возвращающей число, и тип функции, принимающей строку в качестве аргумента и возвращающей строку, то можно использовать пересечение этих двух типов. для описания (перегруженных) функций, которые выполняют те или иные функции, в зависимости от того, какой тип ввода они даны.number => number
string => string
Современные языки программирования, включая Ceylon , Flow, Java , Scala , TypeScript и Whiley (см. Сравнение языков с типами пересечений ), используют типы пересечений для объединения спецификаций интерфейсов и выражения специального полиморфизма . Дополняя параметрический полиморфизм , типы пересечений могут использоваться, чтобы избежать загрязнения иерархии классов из -за сквозных проблем и уменьшить шаблонный код , как показано в примере TypeScript ниже.
Типа Теоретико исследование типов пересечений называется как типа пересечения дисциплины . [3] Примечательно, что завершение программы можно точно охарактеризовать с помощью типов пересечений. [4]
Пример TypeScript [ править ]
TypeScript поддерживает типы пересечения [5], улучшая выразительность системы типов и уменьшая потенциальный размер иерархии классов, что демонстрируется следующим образом.
Следующее программный код определяет классы Chicken
, Cow
и RandomNumberGenerator
что каждый имеет метод produce
возвращение объекта любого типа Egg
, Milk
или number
. Кроме того, для функций eatEgg
и drinkMilk
требуются аргументы типа Egg
и Milk
соответственно.
class Egg { частный вид : "Яйцо" } class Milk { частный вид : "Milk" }// производит яйца class Chicken { product () { return new Egg (); } }// производит молоко класса Cow { roduct () { return new Milk (); } }// производит случайное число class RandomNumberGenerator { произвести () { return Math . случайный (); } }// требуется функция яйца eatEgg ( egg : Egg ) { return «Я съел яйцо». ; }// требуется функция молока drinkMilk ( milk : Milk ) { return "Я выпил немного молока." ; }
Следующий программный код определяет специальную полиморфную функцию, animalToFood
которая вызывает функцию-член produce
данного объекта animal
. Функция animalToFood
имеет аннотации двух типов, а именно и , связанные через конструктор типа пересечения . В частности, при применении к аргументу типа возвращает объект типа типа , а при применении к аргументу типа возвращает объект типа типа . В идеале он не должен применяться к любому объекту, имеющему (возможно, случайно) метод.((_: Chicken) => Egg)
((_: Cow) => Milk)
&
animalToFood
Chicken
Egg
Cow
Milk
animalToFood
produce
// получая курицу, производит яйцо; дано корове, дает молоко let animalToFood : (( _ : Chicken ) => Egg ) & (( _ : Cow ) => Milk ) = function ( animal : any ) { return animal . произвести (); };
Наконец, следующий программный код демонстрирует типобезопасное использование приведенных выше определений.
var курица = новая курица ();var cow = новая корова ();var randomNumberGenerator = новый RandomNumberGenerator ();консоль . журнал ( курица . продукция ()); //Яйцо { }консоль . журнал ( корова . производить ()); //Молоко { }консоль . журнал ( randomNumberGenerator . произвести ()); //0.2626353555444987консоль . журнал ( animalToFood ( курица )); //Яйцо { }консоль . журнал ( animalToFood ( корова )); //Молоко { }//console.log(animalToFood(randomNumberGenerator)); // ОШИБКА: аргумент типа 'RandomNumberGenerator' не может быть назначен параметру типа 'Cow'консоль . журнал ( eatEgg ( animalToFood ( курица ))); // Я съел яйцо.//console.log(eatEgg(animalToFood(cow))); // ОШИБКА: аргумент типа 'Milk' не может быть назначен параметру типа 'Egg'консоль . журнал ( drinkMilk ( animalToFood ( cow ))); // Я выпил молока.//console.log(drinkMilk(animalToFood(chicken))); // ОШИБКА: аргумент типа «Яйцо» нельзя присвоить параметру типа «Молоко»
Приведенный выше программный код имеет следующие свойства:
- Строки 1–3 создают объекты
chicken
,cow
иrandomNumberGenerator
соответствующего им типа. - Строки 5-7 печатают для ранее созданных объектов соответствующие результаты (предоставленные в виде комментариев) при вызове
produce
. - Строка 9 (соответственно 10) демонстрирует типобезопасное использование метода,
animalToFood
примененного кchicken
(соответственноcow
). - Строка 11, если она не зафиксирована, приведет к ошибке типа во время компиляции. Хотя реализация в
animalToFood
мог вызватьproduce
методrandomNumberGenerator
, то аннотацию типа изanimalToFood
Запрещает его. Это соответствует предполагаемому значениюanimalToFood
. - Строка 13 (соответственно 15) демонстрирует, что применение
animalToFood
кchicken
(соответственноcow
) приводит к объекту типаEgg
(соответственноMilk
). - Строка 14 (соотв. 16) демонстрирует, что применение
animalToFood
кcow
(соотв.chicken
) Не приводит к объекту типаEgg
(соотв.Milk
). Следовательно, если раскомментировать строку 14 (соответственно, 16), это приведет к ошибке типа во время компиляции.
Сравнение с наследованием [ править ]
Приведенный выше минималистичный пример может быть реализован с использованием наследования , например, путем наследования классов Chicken
и Cow
от базового класса Animal
. Однако в более крупных условиях это может быть невыгодно. Введение новых классов в иерархию классов не обязательно оправдано для сквозных проблем или может быть совершенно невозможно, например, при использовании внешней библиотеки. Вообразимо, приведенный выше пример можно было бы расширить следующими классами:
- класс
Horse
, не имеющийproduce
метода; - класс
Sheep
, у которого естьproduce
метод, возвращающийWool
; - класс
Pig
, у которого естьproduce
метод, который можно использовать только один раз, возвращаясьMeat
.
Для этого могут потребоваться дополнительные классы (или интерфейсы), указывающие, доступен ли метод создания, возвращает ли метод производства еду и можно ли использовать этот метод повторно. В целом это может загрязнить иерархию классов.
Сравнение с уткой [ править ]
Приведенный выше минималистский пример уже показывает, что утиная печать менее подходит для реализации данного сценария. Хотя класс RandomNumberGenerator
содержит produce
метод, объект randomNumberGenerator
не должен быть допустимым аргументом для animalToFood
. Приведенный выше пример может быть реализован с использованием утиного ввода, например, путем введения нового поля argumentForAnimalToFood
в классы Chicken
и Cow
указания того, что объекты соответствующего типа являются допустимыми аргументами для animalToFood
. Однако это не только увеличит размер соответствующих классов (особенно с введением большего количества методов, похожих на animalToFood
), но также является нелокальным подходом по отношению к animalToFood
.
Сравнение с перегрузкой функций [ править ]
Приведенный выше пример может быть реализован с использованием перегрузки функций , например, путем реализации двух методов и . В TypeScript такое решение практически идентично приведенному примеру. Другие языки программирования, такие как Java , требуют отдельной реализации перегруженного метода. Это может привести либо к дублированию кода, либо к шаблонному коду .animalToFood(animal: Chicken): Egg
animalToFood(animal: Cow): Milk
Сравнение с шаблоном посетителя [ править ]
Приведенный выше пример можно реализовать с помощью шаблона посетителя . Это потребовало бы, чтобы каждый класс животных реализовал accept
метод, принимающий объект, реализующий интерфейс AnimalVisitor
(добавляя нелокальный шаблонный код ). Функция animalToFood
будет реализована как visit
метод реализации AnimalVisitor
. К сожалению, трудно представить связь между типом ввода ( Chicken
или Cow
) и типом результата ( Egg
или Milk
).
Ограничения [ править ]
С одной стороны, типы пересечения могут использоваться для локального аннотирования различных типов функции без введения новых классов (или интерфейсов) в иерархию классов. С другой стороны, этот подход требует явного указания всех возможных типов аргументов и типов результатов. Если поведение функции может быть точно задано унифицированным интерфейсом, параметрическим полиморфизмом или утиной типизацией , то многословный характер типов пересечений неблагоприятен. Следовательно, типы пересечений следует рассматривать как дополнительные к существующим методам спецификации.
Зависимый тип перекрестка [ править ]
Зависит тип пересечения , обозначенный , является зависимым типом , в которых тип может зависеть от термина переменного . [6] В частности, если терм имеет зависимый тип пересечения , тогда у этого термина есть и тип, и тип , где - это тип, который является результатом замены всех вхождений переменной термина в термином .
Пример Scala [ править ]
Scala поддерживает объявления типов [7] как членов объекта. Это позволяет типу члена объекта зависеть от значения другого члена, что называется зависимым от пути типом . [8]
Например, следующий текст программы определяет типаж Scala Witness
, который можно использовать для реализации одноэлементного шаблона . [9]
черт свидетель { типа Т Val значение : Т {} }
Вышеупомянутый признак Witness
объявляет член T
, которому может быть назначен тип в качестве его значения, и член value
, которому может быть присвоено значение типа T
. Следующий текст программы определяет объект booleanWitness
как экземпляр вышеуказанного признака Witness
. Объект booleanWitness
определяет тип T
как Boolean
и значение value
как true
. Например, печать на консоли.System.out.println(booleanWitness.value)
true
объект booleanWitness расширяет свидетеля { type T = Boolean val value = true }
Позвольте быть типом (в частности, типом записи ) объектов, имеющих член типа . В приведенном выше примере объекту может быть присвоен зависимый тип пересечения . Аргументация следующая. У объекта есть член , которому в качестве значения назначен тип . Поскольку это тип, объект имеет тип . Кроме того, у объекта есть член, которому присвоено значение типа . Поскольку значение равно , объект имеет тип . В целом объект имеет тип пересеченияbooleanWitness
booleanWitness
T
Boolean
Boolean
booleanWitness
booleanWitness
value
true
Boolean
booleanWitness.T
Boolean
booleanWitness
booleanWitness
. Следовательно, представляя самосылку как зависимость, объект booleanWitness
имеет зависимый тип пересечения .
В качестве альтернативы, приведенный выше минималистичный пример можно описать с использованием зависимых типов записей . [10] По сравнению с зависимыми типами пересечений, зависимые типы записей представляют собой строго более специализированное теоретическое понятие типов. [6]
Пересечение типового семейства [ править ]
Пересечение типа семейства , обозначенное , является зависимым типом , в которых тип может зависеть от термина переменного . [6] В частности, если термин имеет тип , то для каждого термина типа этот термин имеет тип . Это понятие также называется неявным типом Pi , [11] заметив , что аргумент не сохраняются на уровне термина.
Сравнение языков с типами пересечений [ править ]
Язык | Активно развивается | Парадигма (ы) | Положение дел | Функции |
---|---|---|---|---|
C # | Да [12] |
| Обсуждается [13] | ? |
Цейлон | Да [14] |
| Поддерживается [15] |
|
F # | Да [16] |
| Обсуждается [17] | ? |
Поток | Да [18] |
| Поддерживается [19] |
|
Форсайт | Нет |
| Поддерживается [20] |
|
Ява | Да [21] |
| Поддерживается [22] |
|
Scala | Да [23] |
| Поддерживается [24] [25] |
|
Машинопись | Да [26] |
| Поддерживается [5] |
|
Пока | Да [27] |
| Поддерживается [28] | ? |
Ссылки [ править ]
- ^ Барендрегт, Хенк; Коппо, Марио; Дезани-Чианкаглини, Мариангиола (1983). «Лямбда-модель фильтра и полнота присвоения типов». Журнал символической логики . 48 (4): 931–940. DOI : 10.2307 / 2273659 . JSTOR 2273659 .
- ^ Palsberg, Jens (2012). «Перегрузка NP-Complete». Логика и семантика программ . Конспект лекций по информатике. 7230 . С. 204–218. DOI : 10.1007 / 978-3-642-29485-3_13 . ISBN 978-3-642-29484-6.
- ^ Хенк Барендрегт; Вил Деккерс; Ричард Статман (20 июня 2013 г.). Лямбда-исчисление с типами . Издательство Кембриджского университета. стр. 1–. ISBN 978-0-521-76614-2.
- ^ Ghilezan, Сильвия (1996). «Сильная нормализация и типизация с типами пересечений» . Журнал формальной логики Нотр-Дам . 37 (1): 44–52. DOI : 10.1305 / ndjfl / 1040067315 .
- ^ a b «Типы пересечений в TypeScript» . Проверено 1 августа 2019 .
- ^ a b c Копылов, Алексей (2003). «Зависимое пересечение: новый способ определения записей в теории типов». 18-й симпозиум IEEE по логике в компьютерных науках . LICS 2003. Компьютерное общество IEEE. С. 86–95. CiteSeerX 10.1.1.89.4223 . DOI : 10,1109 / LICS.2003.1210048 .
- ^ "Объявления типов в Scala" . Проверено 15 августа 2019 .
- ^ Амин, Нада; Грюттер, Самуэль; Одерский, Мартин; Ромпф, Тиарк; Штуки, Сандро (2016). «Суть зависимых типов объектов». Список успехов, которые могут изменить мир - эссе, посвященные Филиппу Вадлеру по случаю его 60-летия . Конспект лекций по информатике . 9600 . Springer. С. 249–272. DOI : 10.1007 / 978-3-319-30936-1_14 .
- ^ "Синглтоны в бесформенной библиотеке Scala" . Проверено 15 августа 2019 .
- ^ Поллак, Роберт (2000). «Зависимо типизированные записи для представления математической структуры». Доказательство теорем в логиках высокого порядка, 13-я международная конференция . TPHOLs 2000. Springer. С. 462–479. DOI : 10.1007 / 3-540-44659-1_29 .
- ^ Пень, Аарон (2018). «От реализуемости к индукции через зависимое пересечение». Летопись чистой и прикладной логики . 169 (7): 637–655. DOI : 10.1016 / j.apal.2018.03.002 .
- ^ "Руководство по C #" . Проверено 8 августа 2019 .
- ^ «Обсуждение: типы объединений и пересечений в C Sharp» . Проверено 8 августа 2019 .
- ^ «Затмение Цейлона: Добро пожаловать на Цейлон» . Проверено 8 августа 2019 .
- ^ "Типы пересечений на Цейлоне" . Проверено 8 августа 2019 .
- ^ "F # Software Foundation" . Проверено 8 августа 2019 .
- ^ "Добавить типы пересечений к F Sharp" . Проверено 8 августа 2019 .
- ^ «Поток: средство проверки статического типа для JavaScript» . Проверено 8 августа 2019 .
- ^ «Синтаксис типа пересечения в потоке» . Проверено 8 августа 2019 .
- Перейти ↑ Reynolds, JC (1988). Эскизный проект языка программирования Forsythe.
- ^ «Программное обеспечение Java» . Проверено 8 августа 2019 .
- ^ "IntersectionType (Java SE 12 и JDK 12)" . Проверено 1 августа 2019 .
- ^ "Язык программирования Scala" . Проверено 8 августа 2019 .
- ^ «Составные типы в Scala» . Проверено 1 августа 2019 .
- ^ "Типы пересечения в Dotty" . Проверено 1 августа 2019 .
- ^ «TypeScript - масштабируемый JavaScript» . Проверено 1 августа 2019 .
- ^ «В то время как: язык программирования с открытым исходным кодом с расширенной статической проверкой» . Проверено 1 августа 2019 .
- ^ «Спецификация языка Whyyy» (PDF) . Проверено 1 августа 2019 .