Система типов - Type system

В языки программирования, а система типов это логическая система состоящий из набора правил, который назначает свойство, называемое тип к различным конструкциям компьютерная программа, такие как переменные, выражения, функции или модули.[1] Эти типы формализуют и обеспечивают соблюдение неявных категорий, которые программист использует для алгебраические типы данных, структуры данных или другие компоненты (например, «строка», «массив с плавающей запятой», «функция, возвращающая логическое значение»). Основная цель системы типов - уменьшить возможности для ошибки в компьютерных программах[2] определяя интерфейсы между различными частями компьютерной программы, а затем проверка того, что части были соединены согласованным образом. Эта проверка может происходить статически (при время компиляции ), динамически (при время выполнения ) или их комбинацию. Системы типов имеют и другие цели, такие как выражение бизнес-правил, включение определенных оптимизаций компилятора, разрешение множественная отправка, предоставляя форму документации и т. д.

Система типов связывает тип с каждым вычисленным значением и, исследуя поток этих значений, пытается гарантировать или доказать, что нет ошибки типа может случиться. Данная система типов, о которой идет речь, определяет, что составляет ошибку типа, но в целом цель состоит в том, чтобы предотвратить операции, ожидающие использования определенного типа значения со значениями, для которых эта операция не имеет смысла (логические ошибки ). Системы типов часто указываются как часть языки программирования и встроены в интерпретаторы и компиляторы, хотя система типов языка может быть расширена с помощью дополнительные инструменты которые выполняют дополнительные проверки с использованием синтаксиса и грамматики исходного типа языка.

Обзор использования

Примером простой системы типов является Язык C. Части программы C - это функция определения. Одна функция вызывается другой функцией. Интерфейс функции указывает имя функции и список значений, которые передаются в код функции. Код вызывающей функции указывает имя вызываемой функции, а также имена переменных, которые содержат значения для передачи ей. Во время выполнения значения помещаются во временное хранилище, затем выполнение переходит к коду вызванной функции. Код вызываемой функции получает доступ к значениям и использует их. Если инструкции внутри функции написаны с предположением получения целое число значение, но вызывающий код передал значение с плавающей запятой, то вызванная функция вычислит неправильный результат. Компилятор C проверяет типы аргументов, переданных функции, когда она вызывается, по типам параметров, объявленных в определении функции. Если типы не совпадают, компилятор выдает ошибку времени компиляции.

А компилятор может также использовать статический тип значения для оптимизации необходимого хранилища и выбора алгоритмов для операций со значением. Во многих C составляет плавать тип данных, например, представлен в 32 биты, в соответствии с Спецификация IEEE для чисел с плавающей запятой одинарной точности. Таким образом, они будут использовать специфичные для чисел с плавающей запятой микропроцессорные операции по этим значениям (сложение с плавающей запятой, умножение и т. д.).

Глубина ограничений типа и способ их оценки влияют на набор текста языка. А язык программирования может дополнительно связать операцию с различными разрешениями для каждого типа в случае полиморфизм типов. Теория типов это изучение систем типов. Конкретные типы некоторых языков программирования, такие как целые числа и строки, зависят от практических вопросов архитектуры компьютера, реализации компилятора и дизайна языка.

Основы

Формально, теория типов изучает системы типов. Язык программирования должен иметь вхождение для проверки типа с помощью система типов будь то во время компиляции или во время выполнения, с аннотациями вручную или автоматически. В качестве Марк Манассе короче говоря:[3]

Фундаментальная проблема, решаемая теорией типов, - обеспечение того, чтобы программы имели смысл. Основная проблема, вызванная теорией типов, состоит в том, что значимые программы могут не иметь значения, приписываемого им. Стремление к более богатой типовой системе является результатом этого противоречия.

Назначение типа данных, называемого набор текста, придает смысл последовательности биты например, значение в объем памяти или несколько объект например, Переменная. Аппаратное обеспечение компьютер общего назначения не может различить, например, адрес памяти и код инструкции, или между персонаж, целое число, или число с плавающей запятой, потому что он не делает внутреннего различия между любыми возможными значениями, которые последовательность битов может иметь в виду.[примечание 1] Связывание последовательности битов с типом означает, что смысл к программируемому оборудованию, чтобы сформировать символическая система состоит из этого оборудования и некоторой программы.

Программа связывает каждое значение по крайней мере с одним конкретным типом, но также может случиться так, что одно значение связано со многими подтипы. Другие объекты, такие как объекты, модули, каналы связи и зависимости могут стать ассоциированными с типом. Даже тип может быть связан с типом. Реализация система типов теоретически может ассоциировать отождествления, называемые тип данных (тип значения), класс (тип объекта), и видтип типа, или метатип). Это абстракции, через которые может проходить типизация в иерархии уровней, содержащихся в системе.

Когда язык программирования развивает более сложную систему типов, он получает более детализированный набор правил, чем базовая проверка типов, но за это приходится расплачиваться, когда выводы типа (и другие свойства) становятся неразрешимый, и когда программист должен уделять больше внимания аннотированию кода или рассмотрению операций и функционирования, связанных с компьютером. Сложно найти достаточно выразительную систему типов, которая удовлетворяла бы всем методам программирования в тип безопасный манера.

Чем больше ограничений типа накладывает компилятор, тем больше строго типизированный язык программирования есть. Строго типизированные языки часто требуют от программиста выполнения явных преобразований в контекстах, где неявное преобразование не причинит вреда. Система типов Паскаля была описана как «слишком сильная», потому что, например, размер массива или строки является частью его типа, что затрудняет некоторые задачи программирования.[4][5] Haskell также строго типизирован, но его типы определяются автоматически, поэтому явные преобразования часто (но не всегда) не нужны.

Компилятор языка программирования также может реализовать зависимый тип или система эффектов, что позволяет проверять типы еще большего количества спецификаций программ. Помимо простых пар значение-тип, виртуальная «область» кода связана с компонентом «эффекта», описывающим Какие в настоящее время делается с чем, и позволяя, например, "выдавать" отчет об ошибке. Таким образом, символическая система может быть система типов и эффектов, что наделяет его большей проверкой безопасности, чем только проверка типов.

Независимо от того, автоматизировано ли это компилятором или задано программистом, система типов делает поведение программы незаконным, если оно выходит за рамки правил системы типов. Преимущества, предоставляемые системами типов, определяемыми программистом, включают:

  • Абстракция (или же модульность) - Типы позволяют программистам думать на более высоком уровне, чем бит или байт, не заботясь о низкоуровневой реализации. Например, программисты могут начать думать о строке как о наборе символьных значений, а не как о простом массиве байтов. Более того, типы позволяют программистам думать и выражать интерфейсы между двумя из любой-размерные подсистемы. Это обеспечивает большее количество уровней локализации, так что определения, необходимые для взаимодействия подсистем, остаются согласованными, когда эти две подсистемы обмениваются данными.
  • Документация - В более выразительных системах шрифтов типы могут служить формой документация прояснение намерений программиста. Например, если программист объявляет функцию как возвращающую тип отметки времени, это документирует функцию, когда тип отметки времени может быть явно объявлен глубже в коде как целочисленный тип.

Преимущества, предоставляемые системами типов, определяемыми компилятором, включают:

  • Оптимизация - Статическая проверка типов может предоставить полезную информацию во время компиляции. Например, если тип требует, чтобы значение было выровнено в памяти с кратностью четырем байтам, компилятор может использовать более эффективные машинные инструкции.
  • Безопасность - Типовая система позволяет компилятор для обнаружения бессмысленного или, возможно, неверного кода. Например, мы можем идентифицировать выражение 3 / "Привет, мир" как недействительный, если в правилах не указано, как разделить целое число по нить. Сильный набор текста обеспечивает большую безопасность, но не может гарантировать полное безопасность типа.

Ошибки типа

Ошибка типа - это непреднамеренное состояние, которое может проявляться на нескольких этапах разработки программы. Таким образом, в системе типов требуется средство обнаружения ошибки. На некоторых языках, таких как Haskell, для которых вывод типа автоматизирован, ворсинок может быть доступен его компилятору для помощи в обнаружении ошибки.

Безопасность типов способствует правильность программы, но может гарантировать правильность только за счет того, что сама проверка типов неразрешимая проблема.[нужна цитата ] В система типов при автоматической проверке типов программа может работать некорректно, но при этом безопасно набираться и не вызывать ошибок компилятора. Деление на ноль небезопасная и неправильная операция, но проверка типов работает на время компиляции только не сканирует для деления на ноль в большинстве языков, и тогда он остается как ошибка выполнения. Чтобы доказать отсутствие этих дефектов более общего, чем типов, других типов формальные методы, известные как программный анализ, широко используются. В качестве альтернативы, достаточно выразительная система типов, например, в языках с зависимой типизацией, может предотвратить подобные ошибки (например, выражение тип ненулевых чисел). Кроме того тестирование программного обеспечения является эмпирический метод поиска ошибок, которые не может обнаружить средство проверки типов.

Проверка типа

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

Статическая проверка типа

Статическая проверка типа - это процесс проверки безопасность типа программы на основе анализа текста программы (исходного кода). Если программа проходит проверку статического типа, то программа гарантированно удовлетворяет некоторому набору свойств безопасности типа для всех возможных входных данных.

Статическую проверку типа можно рассматривать как ограниченную форму проверка программы (увидеть безопасность типа ), а в типобезопасном языке также можно рассматривать как оптимизацию. Если компилятор может доказать, что программа хорошо типизирована, то ему не нужно производить динамические проверки безопасности, что позволяет скомпилированному двоичному файлу работать быстрее и иметь меньший размер.

Статическая проверка типов для полных по Тьюрингу языков по своей сути консервативна. То есть, если система типов является как звук (это означает, что он отклоняет все неправильные программы) и разрешимый (это означает, что можно написать алгоритм, который определяет, хорошо ли типизирована программа), тогда он должен быть неполный (это означает, что есть правильные программы, которые также отклоняются, даже если они не обнаруживают ошибок времени выполнения).[6] Например, рассмотрим программу, содержащую код:

if <сложный тест> then <сделать что-нибудь> else <сигнал об ошибке типа>

Даже если выражение <complex test> всегда оценивает истинный во время выполнения большинство средств проверки типов отклонят программу как некорректно типизированную, потому что статическому анализатору трудно (если не невозможно) определить, что еще ветку брать не будут.[7] И наоборот, средство проверки статического типа быстро обнаружит ошибки типа в редко используемых путях кода. Без проверки статического типа даже покрытие кода тесты со 100% покрытием могут не найти ошибок такого типа. Тесты могут не обнаружить такие ошибки типа, потому что необходимо учитывать комбинацию всех мест, где создаются значения, и всех мест, где используется определенное значение.

Ряд полезных и распространенных функций языка программирования нельзя проверить статически, например понижение. Таким образом, во многих языках есть как статическая, так и динамическая проверка типов; средство проверки статического типа проверяет, что может, а динамические проверки проверяют все остальное.

Многие языки со статической проверкой типов предоставляют способ обойти проверку типов. Некоторые языки позволяют программистам выбирать между безопасностью статического и динамического типов. Например, C # различает статически типизированный и динамически типизированный переменные. Использование первых проверяется статически, тогда как использование последних проверяется динамически. Другие языки позволяют писать код, который не является типобезопасным; например, в C, программисты могут свободно преобразовывать значение между любыми двумя типами, имеющими одинаковый размер, эффективно подрывая концепцию типа.

Список языков с проверкой статического типа см. категория для статически типизированных языков.

Проверка динамического типа и информация о типе среды выполнения

Динамическая проверка типов - это процесс проверки типовой безопасности программы во время выполнения. Реализации языков с динамической проверкой типов обычно связывают каждый объект среды выполнения с тег типа (то есть ссылка на тип), содержащая информацию о его типе. Эта информация о типе среды выполнения (RTTI) также может использоваться для реализации динамическая отправка, позднее связывание, понижение, отражение и аналогичные функции.

Большинство языков с типобезопасностью включают некоторую форму проверки динамического типа, даже если в них также есть средство проверки статического типа.[нужна цитата ] Причина этого в том, что многие полезные функции или свойства трудно или невозможно проверить статически. Например, предположим, что программа определяет два типа, A и B, где B является подтипом A. Если программа пытается преобразовать значение типа A в тип B, который известен как понижение, то операция допустима только в том случае, если преобразуемое значение на самом деле является значением типа B. Таким образом, необходима динамическая проверка, чтобы убедиться, что операция безопасна. Это требование является одним из критических замечаний по поводу понижения.

По определению, проверка динамического типа может вызвать сбой программы во время выполнения. В некоторых языках программирования можно предвидеть эти сбои и исправлять их. В других случаях ошибки проверки типов считаются фатальными.

Языки программирования, которые включают проверку динамического типа, но не проверку статического типа, часто называют «языками программирования с динамической типизацией». Список таких языков см. категория для динамически типизированных языков программирования.

Сочетание статической и динамической проверки типов

Некоторые языки допускают как статическую, так и динамическую типизацию. Например, Java и некоторые другие якобы статически типизированные языки поддерживают понижение типы их подтипы, запрашивая объект, чтобы обнаружить его динамический тип и другие операции типа, которые зависят от информации о типе среды выполнения. Другой пример C ++ RTTI. В более общем смысле, большинство языков программирования включают механизмы для отправки по различным «типам» данных, например непересекающиеся союзы, полиморфизм времени выполнения, и типы вариантов. Даже когда они не взаимодействуют с аннотациями типов или проверкой типов, такие механизмы по существу аналогичны реализациям динамической типизации. Видеть язык программирования для более подробного обсуждения взаимодействия между статической и динамической типизацией.

Доступ к объектам в объектно-ориентированных языках обычно осуществляется по ссылке, статический целевой тип (или тип манифеста) которой равен либо типу времени выполнения объекта (его скрытому типу), либо его супертипу. Это соответствует Принцип подстановки Лискова, в котором говорится, что все операции, выполняемые с экземпляром данного типа, также могут выполняться с экземпляром подтипа. Эта концепция также известна как подчинение или полиморфизм подтипа. В некоторых языках подтипы могут также иметь ковариантный или контравариантный возвращаемые типы и типы аргументов соответственно.

Некоторые языки, например Clojure, Common Lisp, или же Cython по умолчанию проверяются динамически, но позволяют программам выбирать статическую проверку типа, предоставляя дополнительные аннотации. Одна из причин использования таких подсказок - это оптимизация производительности критических разделов программы. Это формализовано постепенный набор текста. Среда программирования DrRacket, педагогическая среда, основанная на Лиспе, и предшественник языка Ракетка также мягкий тип.

И наоборот, начиная с версии 4.0, язык C # предоставляет способ указать, что переменная не должна подвергаться статической проверке типа. Переменная, тип которой динамичный не будет подвергаться статической проверке типа. Вместо этого программа полагается на информацию о типе среды выполнения, чтобы определить, как можно использовать переменную.[8]

Проверка статических и динамических типов на практике

Выбор между статической и динамической типизацией требует определенных компромиссы.

Статическая типизация позволяет надежно находить типовые ошибки во время компиляции, что должно повысить надежность поставляемой программы. Однако программисты расходятся во мнениях относительно того, как часто возникают ошибки типов, что приводит к дальнейшим разногласиям по поводу доли кодируемых ошибок, которые могут быть обнаружены путем надлежащего представления разработанных типов в коде.[9][10] Сторонники статической типизации[ВОЗ? ] полагают, что программы более надежны, если они хорошо проверены типом, в то время как динамический набор[ВОЗ? ] указывают на распределенный код, который доказал свою надежность, и на небольшие базы данных ошибок.[нужна цитата ] Значит, ценность статической типизации, по-видимому,[нечеткий ] увеличивается по мере увеличения прочности системы типов. Защитники зависимая типизация,[ВОЗ? ] реализовано на таких языках, как Зависимый ML и Эпиграмма, предположили, что почти все ошибки можно рассматривать как ошибки типа, если типы, используемые в программе, правильно объявлены программистом или правильно выведены компилятором.[11]

Статическая типизация обычно приводит к тому, что скомпилированный код выполняется быстрее. Когда компилятор знает точные типы данных, которые используются (что необходимо для статической проверки, посредством объявления или вывода), он может создать оптимизированный машинный код. Некоторые языки с динамической типизацией, такие как Common Lisp по этой причине разрешить необязательные объявления типов для оптимизации.

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

Статически типизированные языки без вывод типа (такие как C и Ява до версия 10 ) требуют, чтобы программисты объявляли типы, которые должен использовать метод или функция. Это может служить добавленной программной документацией, которая является активной и динамической, а не статической. Это позволяет компилятору предотвратить его нарушение синхронности и игнорирование программистами. Однако язык может быть статически типизирован без объявления типов (примеры включают Haskell, Scala, OCaml, F #, и в меньшей степени C # и C ++ ), поэтому явное объявление типа не является обязательным требованием для статической типизации на всех языках.

Динамическая типизация допускает конструкции, которые некоторые проверки статического типа могут отклонить как недопустимые. Например, оценка становятся возможными функции, выполняющие произвольные данные в виде кода. An оценка функция возможна со статической типизацией, но требует расширенного использования алгебраические типы данных. Кроме того, динамическая типизация лучше приспособлена к переходному коду и прототипированию, например, позволяет использовать структуру данных-заполнителей (фиктивный объект ) для прозрачного использования вместо полной структуры данных (обычно в целях экспериментов и тестирования).

Динамическая типизация обычно позволяет утка печатать (что позволяет более легкое повторное использование кода ). Много[уточнить ] языки со статической типизацией также имеют утка печатать или другие механизмы, такие как общее программирование которые также упрощают повторное использование кода.

Динамическая типизация обычно делает метапрограммирование проще в использовании. Например, Шаблоны C ++ обычно более громоздко писать, чем эквивалент Рубин или Python код с C ++ имеет более строгие правила относительно определений типов (как для функций, так и для переменных). Это заставляет разработчика писать больше шаблонный код для шаблона, чем потребуется разработчику Python. Более продвинутые конструкции времени выполнения, такие как метаклассы и самоанализ часто труднее использовать в языках со статической типизацией. На некоторых языках также могут использоваться такие функции, например для генерации новых типов и моделей поведения на лету на основе данных времени выполнения. Такие сложные конструкции часто предоставляются языки динамического программирования; многие из них динамически типизированы, хотя динамическая типизация не обязательно иметь отношение к языки динамического программирования.

Системы сильного и слабого типов

Языки часто в просторечии называют строго типизированный или слабо типизированный. На самом деле не существует общепринятого определения того, что означают эти термины. В общем, существуют более точные термины для обозначения различий между системами типов, которые заставляют людей называть их «сильными» или «слабыми».

Безопасность типов и безопасность памяти

Третий способ категоризации системы типов языка программирования - безопасность типизированных операций и преобразований. Компьютерные ученые используют термин типобезопасный язык для описания языков, которые не допускают операций или преобразований, нарушающих правила системы типов.

Компьютерные ученые используют термин безопасный для памяти язык (или просто безопасный язык) для описания языков, которые не позволяют программам обращаться к памяти, не предназначенной для их использования. Например, безопасный для памяти язык будет проверить границы массива или статически гарантировать (то есть во время компиляции перед выполнением), что доступ к массиву за пределами массива вызовет ошибки времени компиляции и, возможно, времени выполнения.

Рассмотрим следующую программу на языке, который является безопасным как для типов, так и для памяти:[12]

var x: = 5; var y: = "37"; var z: = x + y;

В этом примере переменная z будет иметь значение 42. Хотя это может быть не то, что ожидал программист, это вполне определенный результат. Если у были бы другой строкой, которая не могла быть преобразована в число (например, "Hello World"), результат также будет четко определен. Обратите внимание, что программа может быть безопасна по типу или памяти и по-прежнему давать сбой при недопустимой операции; фактически, если программа встречает операцию, которая не является типобезопасной, завершение программы часто является единственным вариантом.

Теперь рассмотрим аналогичный пример на C:

int Икс = 5;char у[] = "37";char* z = Икс + у;

В этом примере z будет указывать на адрес памяти пятью символами сверх у, что эквивалентно трем символам после завершающего нулевого символа строки, на которую указывает у. Это память, к которой программа не должна обращаться. Он может содержать мусорные данные и уж точно не содержит ничего полезного. Как показывает этот пример, C не является ни безопасным для памяти, ни типобезопасным языком.

В общем, безопасность типов и безопасность памяти идут рука об руку. Например, язык, поддерживающий арифметику указателя и преобразование числа в указатель (например, C), не является ни безопасным для памяти, ни типобезопасным, поскольку он позволяет обращаться к произвольной памяти, как если бы это была действительная память любого типа.

Для получения дополнительной информации см. безопасность памяти.

Переменные уровни проверки типов

Некоторые языки позволяют применять разные уровни проверки к разным областям кода. Примеры включают:

  • В использовать строгий директива в JavaScript[13][14][15] и Perl применяет более строгую проверку.
  • В объявить (strict_types = 1) в PHP[16] для каждого файла позволяет принимать только переменную точного типа объявления типа, или TypeError будет брошен.
  • В Параметр строго включен в VB.NET позволяет компилятору требовать преобразования между объектами.

Дополнительные инструменты, такие как ворсинок и IBM Rational Purify также можно использовать для достижения более высокого уровня строгости.

Системы дополнительных типов

Это было предложено, главным образом, Гилад Браха, что выбор системы типов должен быть сделан независимо от выбора языка; что система типов должна быть модулем, который может быть засорен на язык по мере необходимости. Он считает, что это выгодно, потому что то, что он называет обязательными системами типов, делает языки менее выразительными, а код - более хрупким.[17] Требование, чтобы типы не влияли на семантику языка, выполнить сложно.

Необязательный набор текста связан с, но отличается от постепенный набор текста. Хотя обе дисциплины набора текста могут использоваться для статического анализа кода (статическая типизация ), системы необязательных типов не обеспечивают безопасность типов во время выполнения (динамическая типизация ). [17][18]

Полиморфизм и типы

Период, термин полиморфизм относится к способности кода (особенно функций или классов) воздействовать на значения нескольких типов или к способности разных экземпляров одной и той же структуры данных содержать элементы разных типов. Системы типов, допускающие полиморфизм, обычно делают это, чтобы улучшить возможность повторного использования кода: на языке с полиморфизмом программистам нужно только реализовать структуру данных, такую ​​как список или ассоциативный массив один раз, а не один раз для каждого типа элемента, с которым они планируют его использовать. По этой причине компьютерные ученые иногда называют использование определенных форм полиморфизма общее программирование. Теоретико-типовые основы полиморфизма тесно связаны с основами полиморфизма. абстракция, модульность и (в некоторых случаях) подтип.

Системы специализированного типа

Было создано множество систем типов, которые предназначены для использования в определенных средах с определенными типами данных или для внеполосных статический анализ программы. Часто они основаны на идеях формальных теория типов и доступны только как часть опытных исследовательских систем.

В следующей таблице представлен обзор теоретических концепций типов, используемых в специализированных системах типов. M, N, O диапазон терминов и имен диапазон по типам. (соотв. ) описывает тип, который является результатом замены всех вхождений переменной типа α (соответственно термин переменная Икс) в τ по типу σ (соответственно термин N).

Понятие типаОбозначениеСмысл
ФункцияЕсли M имеет тип и N имеет тип σ, то приложение имеет тип τ.
ТоварЕсли M имеет тип , тогда пара такая, что N имеет тип σ и О имеет тип τ.
СуммаЕсли M имеет тип , то либо это первая инъекция, такая что N имеет тип σ, или же

вторая инъекция такая, что N имеет тип τ.

ПересечениеЕсли M имеет тип , тогда M имеет тип σ и M имеет тип τ.
СоюзЕсли M имеет тип , тогда M имеет тип σ или M имеет тип τ.
ЗаписыватьЕсли M имеет тип , тогда M есть член Икс который имеет тип τ.
ПолиморфныйЕсли M имеет тип , тогда M имеет тип для любого типа σ.
ЭкзистенциальныйЕсли M имеет тип , тогда M имеет тип для какого-то типа σ.
РекурсивныйЕсли M имеет тип , тогда M имеет тип .
Зависимая функцияЕсли M имеет тип и N имеет тип σ, то приложение имеет тип .
Зависимый продуктЕсли M имеет тип , тогда пара такая, что N имеет тип σ и О имеет тип .
Зависимый перекресток[19]Если M имеет тип , тогда M имеет тип σ и M имеет тип .
Семейное пересечение[19]Если M имеет тип , тогда M имеет тип на любой срок N типа σ.
Семейный союз[19]Если M имеет тип , тогда M имеет тип на какой-то срок N типа σ.

Зависимые типы

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

куда k, м, п - произвольные положительные целые числа. Вариант ML называется Зависимый ML был создан на основе этой системы типов, но поскольку проверка типов для обычных зависимых типов неразрешимый, не все программы, использующие их, можно проверить без каких-либо ограничений. Зависимое машинное обучение ограничивает вид равенства, которое может решить Арифметика пресбургера.

Другие языки, такие как Эпиграмма сделать значение всех выражений на языке разрешимым, чтобы проверка типов была разрешимой. Однако в целом доказательство разрешимости неразрешимо, поэтому многие программы требуют написанных от руки аннотаций, которые могут быть весьма нетривиальными. Поскольку это препятствует процессу разработки, многие языковые реализации предоставляют простой выход в виде опции для отключения этого условия. Однако это происходит за счет того, что проверка типов запускается в бесконечный цикл когда загружаются программы, которые не проверяют тип, что приводит к сбою компиляции.

Линейные типы

Линейные типы, основанный на теории линейная логика, и тесно связан с типы уникальности, являются типами, присвоенными значениям, имеющим свойство, что у них всегда есть одна и только одна ссылка на них. Они полезны для описания больших неизменные ценности такие как файлы, строки и т. д., потому что любая операция, которая одновременно разрушает линейный объект и создает аналогичный объект (например, 'str = str + "а"') можно оптимизировать «под капотом» до мутации на месте. Обычно это невозможно, поскольку такие мутации могут вызывать побочные эффекты в частях программы, содержащих другие ссылки на объект, нарушая ссылочная прозрачность. Они также используются в прототипе операционной системы. Сингулярность для межпроцессного взаимодействия, статически гарантируя, что процессы не могут совместно использовать объекты в общей памяти, чтобы предотвратить состояние гонки. В Чистый язык (а Haskell -подобный язык) использует эту систему типов, чтобы получить большую скорость (по сравнению с выполнением глубокого копирования), оставаясь при этом безопасным.

Типы пересечений

Типы пересечений типы, описывающие значения, принадлежащие обе двух других заданных типов с перекрывающимися наборами значений. Например, в большинстве реализаций C знаковый символ имеет диапазон от -128 до 127, а символ без знака имеет диапазон от 0 до 255, поэтому тип пересечения этих двух типов будет иметь диапазон от 0 до 127. Такой тип пересечения можно безопасно передать в функции, ожидающие либо подписанные или беззнаковые символы, поскольку он совместим с обоими типами.

Типы пересечения полезны для описания типов перегруженных функций: например, если "intint"- это тип функций, принимающих целочисленный аргумент и возвращающих целое число, и"плаватьплавать"- это тип функций, принимающих аргумент с плавающей запятой и возвращающих значение с плавающей запятой, то пересечение этих двух типов может использоваться для описания функций, которые выполняют одно или другое, в зависимости от того, какой тип ввода им дан. Такая функция может быть передается в другую функцию, ожидающую "intint"функционировать безопасно; он просто не будет использовать"плаватьплавать"функциональность.

В иерархии подклассов наиболее производным типом является пересечение типа и типа-предка (например, его родительского). Пересечение родственных типов пусто.

Язык Форсайта включает общую реализацию типов пересечений. Ограниченная форма типы уточнения.

Типы союзов

Типы союзов типы, описывающие значения, принадлежащие либо двух типов. Например, в C знаковый char имеет диапазон от -128 до 127, а неподписанный char имеет диапазон от 0 до 255, поэтому объединение этих двух типов будет иметь общий «виртуальный» диапазон от -128 до 255, который может использоваться частично в зависимости от того, к какому члену объединения осуществляется доступ. Любая функция, обрабатывающая этот тип объединения, должна иметь дело с целыми числами в этом полном диапазоне. В более общем смысле, единственными допустимыми операциями с типом объединения являются операции, действительные для обе типы объединяются. Концепция «объединения» в языке C аналогична типам объединения, но не является типобезопасной, поскольку разрешает операции, допустимые для либо тип, а не обе. Типы объединения важны при анализе программ, где они используются для представления символьных значений, точная природа которых (например, значение или тип) неизвестна.

В иерархии подклассов объединение типа и типа-предка (например, его родительского) является типом-предком. Объединение одноуровневых типов является подтипом их общего предка (то есть все операции, разрешенные для их общего предка, разрешены для типа объединения, но они также могут иметь другие допустимые общие операции).

Экзистенциальные типы

Экзистенциальный типы часто используются в связи с типы записей представлять модули и абстрактные типы данных, из-за их способности отделить реализацию от интерфейса. Например, тип «T = ∃X {a: X; f: (X → int);}» описывает интерфейс модуля, имеющий член данных с именем а типа Икс и функция с именем ж который принимает параметр одно и тоже тип Икс и возвращает целое число. Это можно было реализовать по-разному; Например:

  • intT = {a: int; f: (int → int); }
  • floatT = {a: float; f: (float → int); }

Оба эти типа являются подтипами более общего экзистенциального типа T и соответствуют конкретным типам реализации, поэтому любое значение одного из этих типов является значением типа T. Учитывая значение «t» типа «T», мы знаем, что « tf (ta) "хорошо типизирован, независимо от того, какой абстрактный тип Икс является. Это дает гибкость для выбора типов, подходящих для конкретной реализации, в то время как клиенты, использующие только значения типа интерфейса - экзистенциального типа - изолированы от этих вариантов.

Как правило, для проверки типов невозможно сделать вывод, к какому экзистенциальному типу принадлежит данный модуль. В приведенном выше примере intT {a: int; е: (интервал → интервал); } также может иметь тип ∃X {a: X; f: (int → int); }. Самое простое решение - аннотировать каждый модуль его предполагаемым типом, например:

  • intT = {a: int; f: (int → int); } так как ∃X {a: X; f: (X → int); }

Хотя абстрактные типы данных и модули были реализованы в языках программирования в течение некоторого времени, только в 1988 г. Джон С. Митчелл и Гордон Плоткин установил формальную теорию под лозунгом: «Абстрактные типы [данных] имеют экзистенциальный тип».[20] Теория второго порядка типизированное лямбда-исчисление похожий на Система F, но с экзистенциальной, а не универсальной количественной оценкой.

Постепенный набор текста

Постепенный набор текста это система типов, в которой переменным может быть присвоен тип либо в время компиляции (что является статической типизацией) или в время выполнения (что является динамической типизацией), позволяя разработчикам программного обеспечения выбирать любую парадигму типа в зависимости от конкретного языка.[21] В частности, при постепенной типизации используется специальный тип с именем динамичный для представления статически неизвестных типов, а постепенная типизация заменяет понятие равенства типов новым отношением, называемым последовательность что связывает динамический тип со всеми остальными типами. Отношение согласованности симметрично, но не транзитивно.[22]

Явное или неявное объявление и вывод

Многие системы статических типов, такие как C и Java, требуют объявления типов: программист должен явно связать каждую переменную с определенным типом. Другие, такие как Haskell's, выполняют вывод типа: компилятор делает выводы о типах переменных на основе того, как программисты используют эти переменные. Например, учитывая функцию ж(Икс, у) это добавляет Икс и у вместе компилятор может сделать вывод, что Икс и у должны быть числами, поскольку сложение определяется только для чисел. Таким образом, любой вызов ж в другом месте программы, указывающее нечисловой тип (например, строку или список) в качестве аргумента, будет сигнализировать об ошибке.

Числовые и строковые константы и выражения в коде могут и часто подразумевают тип в определенном контексте. Например, выражение 3.14 может подразумевать тип плавающая точка, в то время как [1, 2, 3] может означать список целых чисел - обычно множество.

Вывод типа вообще возможен, если он вычислимый в рассматриваемой системе типов. Более того, даже если вывод не вычислим в целом для данной системы типов, вывод часто возможен для большого подмножества реальных программ. Система типов Haskell, версия Хиндли-Милнер, является ограничением Система Fω к так называемым полиморфным типам ранга 1, в которых вывод типа вычислим. Большинство компиляторов Haskell допускают полиморфизм произвольного ранга в качестве расширения, но это делает вывод типа невычислимым. (Проверка типа разрешимый однако программы с рангом 1 по-прежнему имеют вывод типов; Полиморфные программы более высокого ранга отклоняются, если не указаны явные аннотации типа.)

Проблемы с решением

Система типов, которая присваивает типы терминам в средах типов, используя правила типа естественно ассоциируется с проблемы решения из проверка типа, типичность, и тип проживания.[23]

  • Учитывая типовое окружение , термин , а тип , решите, является ли срок можно присвоить тип в типовой среде.
  • Учитывая срок , решите, существует ли среда типа и тип так что срок можно присвоить тип в типовой среде .
  • Учитывая типовое окружение и тип , решите, существует ли термин которому можно присвоить тип в типовой среде.

Единая система типов

Некоторые языки, такие как C # или Scala имеют единую систему типов.[24] Это означает, что все C # типы, включая примитивные типы, наследуются от одного корневого объекта. Каждый тип в C # наследуется от класса Object. Некоторые языки, например Ява и Раку, имеют корневой тип, но также имеют примитивные типы, не являющиеся объектами.[25] Java предоставляет типы объектов оболочки, которые существуют вместе с примитивными типами, поэтому разработчики могут использовать либо типы объектов оболочки, либо более простые не объектные примитивные типы. Raku автоматически преобразует примитивные типы в объекты при обращении к их методам.[26]

Совместимость: эквивалентность и подтипы

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

Если тип е и тип Икс одинаковы, и присвоение разрешено для этого типа, тогда это допустимое выражение. Таким образом, в простейших системах типов вопрос о совместимости двух типов сводится к вопросу о том, являются ли они совместимыми. равный (или же эквивалент). Однако разные языки имеют разные критерии того, когда два выражения типа понимаются как обозначающие один и тот же тип. Эти разные эквациональные теории типов сильно различаются, два крайних случая системы структурного типа, в котором любые два типа, описывающие значения с одинаковой структурой, эквивалентны, и системы номинативного типа, в котором никакие два синтаксически различных выражения типа не обозначают один и тот же тип (т.е., типы должны иметь одинаковое «имя», чтобы быть равными).

На языках с подтип, отношение совместимости более сложное. В частности, если B это подтип А, то значение типа B может использоваться в контексте, где один из типов А ожидается (ковариантный ), даже если обратное неверно. Как и эквивалентность, отношение подтипа определяется по-разному для каждого языка программирования, с множеством возможных вариаций. Наличие параметрических или специальных полиморфизм в языке также может иметь значение для совместимости типов.

Смотрите также

Примечания

  1. ^ Компьютерная линия Burroughs ALGOL определяла содержимое ячейки памяти по битам флага. Биты флагов определяют содержимое ячейки памяти. Инструкция, тип данных и функции определяются 3-битным кодом в дополнение к его 48-битному содержимому. Только MCP (Master Control Program) может записывать биты кода флага.

Рекомендации

  1. ^ Пирс 2002, п. 1: «Система типов - это управляемый синтаксический метод для доказательства отсутствия определенного поведения программы путем классификации фраз по типам вычисляемых ими значений».
  2. ^ Карделли 2004, п. 1: «Основная цель системы типов - предотвращать возникновение ошибок выполнения во время выполнения программы».
  3. ^ Пирс 2002, п. 208.
  4. ^ Тайсон, Дж. Р. (25 апреля 1983 г.). «JRT заявляет, что виновен - в создании пригодного для использования Паскаля». Инфомир. 5 (1). п. 66.
  5. ^ Керниган, Брайан (1981). «Почему Паскаль не мой любимый язык программирования». Архивировано из оригинал на 2012-04-06. Получено 2011-10-22.
  6. ^ «... любая разумная, разрешимая система типов должна быть неполной» - Д. Реми (2017). п. 29, Реми, Дидье. «Системы типов для языков программирования» (PDF). Получено 26 мая 2013.
  7. ^ Пирс 2002.
  8. ^ "динамический (Справочник по C #)". Библиотека MSDN. Microsoft. Получено 14 января 2014.
  9. ^ Мейер, Эрик; Дрейтон, Питер. «Статический набор текста там, где это возможно, динамический набор текста, когда это необходимо: конец холодной войны между языками программирования» (PDF). Microsoft Корпорация.
  10. ^ Лаучер, Аманда; Снизься, Пол. «Типы против тестов». InfoQ.
  11. ^ Си, Хунвэй (1998). Зависимые типы в практическом программировании (Кандидат наук). Отделение математических наук Университета Карнеги-Меллона. CiteSeerX  10.1.1.41.548.
    Си, Хунвэй; Пфеннинг, Франк (1999). «Зависимые типы в практическом программировании». Материалы 26-го симпозиума ACM SIGPLAN-SIGACT по принципам языков программирования. ACM. С. 214–227. CiteSeerX  10.1.1.69.2042. Дои:10.1145/292540.292560. ISBN  1581130953. S2CID  245490.
  12. ^ Visual Basic является примером языка, который безопасен как для типов, так и для памяти.
  13. ^ «4.2.2 Строгий вариант ECMAScript». Спецификация языка ECMAScript® 2020 (11-е изд.). ECMA. Июнь 2020. ECMA-262.
  14. ^ Строгий режим - JavaScript | MDN. Developer.mozilla.org (03.07.2013). Проверено 17 июля 2013.
  15. ^ Строгий режим (JavaScript). Msdn.microsoft.com. Проверено 17 июля 2013.
  16. ^ Строгий набор текста
  17. ^ а б Браха, Г. «Подключаемые типы» (PDF).
  18. ^ «Конечно. Это называется« постепенный набор текста », и я бы назвал его модным ...» Есть ли язык, допускающий как статическую, так и динамическую типизацию?. переполнение стека. 2012 г.
  19. ^ а б c Копылов, Алексей (2003). «Зависимое пересечение: новый способ определения записей в теории типов». 18-й симпозиум IEEE по логике в компьютерных науках. LICS 2003. Компьютерное общество IEEE. С. 86–95. CiteSeerX  10.1.1.89.4223. Дои:10.1109 / LICS.2003.1210048.
  20. ^ Митчелл, Джон С .; Плоткин, Гордон Д. (июль 1988 г.). «Абстрактные типы имеют экзистенциальный тип» (PDF). ACM Trans. Программа. Lang. Syst. 10 (3): 470–502. Дои:10.1145/44501.45065. S2CID  1222153.
  21. ^ Зик, Джереми. "Что такое постепенный набор текста?".
  22. ^ Зик, Джереми; Таха, Валид (сентябрь 2006 г.). Постепенный набор текста для функциональных языков (PDF). Схема и функциональное программирование 2006. Чикагский университет. С. 81–92.
  23. ^ Барендрегт, Хенк; Деккерс, Вил; Статман, Ричард (20 июня 2013 г.). Лямбда-исчисление с типами. Издательство Кембриджского университета. п. 66. ISBN  978-0-521-76614-2.
  24. ^ «8.2.4 Унификация системы типов». Спецификация языка C # (5-е изд.). ECMA. Декабрь 2017. ECMA-334.
  25. ^ «Родные типы». Документация по Perl 6.
  26. ^ «Числа, § Автобокс». Документация по Perl 6.

дальнейшее чтение

внешняя ссылка