Что такое система типов?

50

Фон

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

Во время моего выступления я упомянул, что виртуальная машина предоставляет систему типов, спросили: «Для чего нужна ваша система типов? ». После ответа мне посмеялся человек, задающий вопрос.

Таким образом, хотя я почти наверняка потеряю репутацию, задавая этот вопрос, я обращаюсь к программистам.

Мое понимание

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

Типы также могут быть использованы для предоставления информации этим программистам. Например, я нахожу это объявление:

function sqrt(double n) -> double;

более полезный, чем этот

sqrt(n)

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

Мой ответ

Итак, после того, как вас спросили "Для чего нужна система типов?" Я ответил следующим образом:

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

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

Последующим вопросом было «А как тип присваивается значению?». Итак, я объяснил, что все значения упакованы и имеют указатель, указывающий на структуру определения типа, которая предоставляет информацию об имени типа, на какие сообщения он отвечает и от каких типов он наследует.

После этого мне посмеялись, и мой ответ был отклонен с комментарием «Это не настоящая система типов».

Итак, если то, что я описал, не квалифицируется как «настоящая система типов», что будет? Прав ли тот человек, что то, что я предоставляю, не может рассматриваться как система типов?

Мэл
источник
19
Когда люди говорят о системах типов, они обычно говорят о статической типизации. Динамическая типизация не очень интересна для людей, которые заботятся о системах типов, потому что она почти ничего не гарантирует. Например, какое значение может содержать переменная x? Что-нибудь.
Доваль
7
Мне было бы интересно услышать, что они должны были сказать, чтобы защитить / объяснить свою реакцию.
Newtopian
18
@Doval Динамическая типизация может гарантировать, что вы не войдете в бессмысленное состояние, выполнив что-то вроде добавления 5 к вашей кошке. Конечно, это не помешает вам попробовать , но, по крайней мере, может остановить его на самом деле и дать вам шанс выяснить, что пошло не так, и предпринять корректирующие действия, то, чего не может по-настоящему типичный язык.
8bittree
10
Человек не согласен с вашим ответом на вопрос «А как тип присваивается значению?». Они хотели услышать о правилах набора текста, а не о диаграммах в виде блоков и указателей. Хотя смеяться было абсолютно грубо.
садовник
10
Смеющийся человек, скорее всего, ревнитель к какому-то конкретному языку (семье) с сильной системой типов (Хаскелл кажется популярным) и высмеивает что-то менее сильное (и, следовательно, игрушечное), чем это, или более сильное (и, следовательно, непрактичное), или просто разные. Вступать в дискуссию с фанатиками опасно и бесполезно. Подобный смех настолько груб, что указывает на такие глубокие проблемы. Тебе повезло, что они не начали проповедовать ...
Хайд

Ответы:

30

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

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

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

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

Telastyn
источник
3
«В зависимости от вашей среды, человек, возможно, хотел больше формализма в вашей системе типов». Это наверное так. Я не концентрировался на том, что я могу доказать с помощью системы типов, а скорее думал об этом как о инструменте. Спасибо за рекомендацию книги!
Mael
1
@Mael Некоторые системы типов используются в качестве логики (см. Логические рамки ). поэтому в основном тип дает формулы, а программы являются доказательствами этих формул (например, тип функции a -> bможно рассматривать как подразумеваемый b , т. е. если вы дадите мне значение типа, aя смогу получить значение типа b). Однако для того, чтобы это было согласованным, язык должен быть полным и, следовательно, не тьюринговым. Таким образом, все системы типов реальной жизни на самом деле определяют противоречивую логику.
Бакуриу
20

Мне нравится ответ @ Telastyn особенно из-за ссылки на академический интерес к формализму.

Позвольте мне добавить к обсуждению.

Что такое система типов?

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

Определения типов обычно поддерживают операторы композиции (например, различные формы соединения, как в структурах, подклассах и дизъюнкции, как в перечислениях, объединениях).

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

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

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

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

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

Эрик Эйдт
источник
Я не думаю, что так называемые системы гибридного типа вообще очень распространены. Какие языки ты имеешь в виду?
садовник
2
@gardenhead, способность понижать производительность не является системной функцией статического типа, поэтому обычно она динамически проверяется во время выполнения.
Эрик Эйдт
1
@gardenhead: большинство статически типизированных языков позволяют отложить ввод во время выполнения, будь то просто с помощью void *указателей C (очень слабых), динамических объектов C # или экзистенциально количественных GADT на Haskell (которые дают вам довольно сильные гарантии, чем статически типизированные значения в большинстве других языки).
оставил около
Правда я забыл про "кастинг". Но кастинг - это просто опора для слабой системы типов.
садовник
@gardenhead Помимо статических языков, предоставляющих динамические параметры, многие динамические языки предоставляют некоторую статическую типизацию. Например, Dart, Python и Hack, все имеют режимы или инструменты для выполнения статического анализа, основанного на концепции «постепенной типизации».
IMSoP
14

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

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

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

обзор

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

Написание программ с системой типов - это как управление автомобилем в отличном состоянии: тормоза работают, двери закрываются, двигатель смазан и т. Д. Написание программ без системы типов - это как езда на мотоцикле без шлема и с колесами из спагетти. Вы абсолютно не контролируете себя.

Обосновать обсуждение, скажем , у нас есть язык с буквенным выражением num[n]и str[s]которые представляют цифру п и строку s, соответственно, и примитивные функции plusи concat, с подразумеваемым смыслом. Понятно, что вы не хотите писать что-то вроде plus "hello" "world"или concat 2 4. Но как мы можем предотвратить это? Априори нет способа отличить цифру 2 от строкового литерала «мир». Мы хотели бы сказать, что эти выражения должны использоваться в разных контекстах; у них есть разные типы.

Языки и типы

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

Синтаксис

Программа это дерево. Не обманывайтесь строками текста, которые вы пишете на компьютере; это просто понятные человеку представления программы. Сама программа представляет собой абстрактное синтаксическое дерево . Например, в C мы могли бы написать:

int square(int x) { 
    return x * x;
 }

Это конкретный синтаксис для программы (фрагмент). Представление дерева:

     function square
     /     |       \
   int   int x    return
                     |
                   times
                  /    \
                 x      x

Язык программирования предоставляет грамматику , определяющий допустимые деревья этого языка (либо конкретного или абстрактного синтаксиса могут быть использованы). Обычно это делается с использованием чего-то вроде нотации BNF. Я предполагаю, что вы сделали это для языка, который вы создали.

Семантика

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

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

Я не буду рассказывать, как определить формальную операционную семантику (детали немного задействованы), но в основном нам нужны такие правила:

  1. num[n] это значение
  2. str[s] это значение
  3. Если num[n1]и num[n2]вычислить до целых чисел n_1$ and $n_2$, thenплюс (num [n1], num [n2]) `, то получится целое число $ n_1 + n_2 $.
  4. Если str[s1]и str[s2]оценивает строки s1 и s2, то concat(str[s1], str[s2])вычисляет строку s1s2.

И т.д. Правила на практике намного более формальны, но вы понимаете суть. Однако вскоре мы столкнемся с проблемой. Что происходит, когда мы пишем следующее:

concat(num[5], str[hello])

Гектометр Это довольно загадка. Мы нигде не определили правила для конкатенации числа со строкой. Мы могли бы попытаться создать такое правило, но мы интуитивно знаем, что эта операция не имеет смысла. Мы не хотим, чтобы эта программа была действительной. И поэтому нас неумолимо ведут к типам.

Типы

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

Давайте приведем несколько примеров. Опять же, как и в случае с правилами оценки, я представлю правила набора текста неформально, но их можно сделать строгими. Вот несколько правил:

  1. Токен формы num[n]имеет тип nat.
  2. Токен формы str[s]имеет тип str.
  3. Если выражение e1имеет тип, natа выражение e2имеет тип nat, то выражение plus(e1, e2)имеет тип nat.
  4. Если выражение e1имеет тип, strа выражение e2имеет тип str, то выражение concat(e1, e2)имеет тип str.

Таким образом, в соответствии с этими правилами plus(num[5], num[2])есть тип имеет тип nat, но мы не можем назначить тип plus(num[5], str["hello"]). Мы говорим, что программа (или выражение) хорошо типизирована, если мы можем назначить ей любой тип, и иначе она плохо напечатана. Система типов является надежной, если все хорошо напечатанные программы могут быть выполнены. Хаскель - это звук; С не является.

Заключение

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

gardenhead
источник
4
+1. Величайший трюк, который когда-либо привлекали энтузиасты динамической типизации, - убедить мир, что у вас могут быть «типы» без системы типов. :-)
ruakh
1
Поскольку вы не можете автоматически проверять что-либо интересное для произвольных программ, каждая система типов должна предоставлять оператор приведения (или моральный эквивалент), иначе она жертвует полнотой Тьюринга. Это включает в себя Haskell , конечно.
Кевин
1
@Kevin Я хорошо знаю теорему Райс, но она не так актуальна, как вы могли бы подумать. Начнем с того, что подавляющее большинство программ не требуют неограниченной рекурсии. Если мы работаем на языке, который имеет только примитивную рекурсию, таком как Система Т Годеля, то мы можем проверить интересные свойства, используя систему типов, включая остановку. Большинство программ в реальном мире довольно просты - я не могу вспомнить, когда в последний раз у меня действительно была потребность в кастинге. Полнота Тьюринга переоценена.
садовод
9
«Динамическая типизация на самом деле не печатает», мне всегда казалось, что классические музыканты говорят «Поп-музыка на самом деле не музыка», или евангелисты говорят: «Католики на самом деле не христиане». Да, статические системы типов являются мощными, захватывающими и важными, а динамическая типизация - это нечто иное. Но (как описывают другие ответы) существует целый ряд полезных вещей, помимо статических систем типов, которые традиционно называются типизацией и которые имеют общие черты. Почему нужно настаивать на том, чтобы наш тип печатания был единственно верным?
Питер ЛеФану Лумсдайн
5
@IMSoP: для чего-то более короткого, чем книга, эссе Криса Смита « Что нужно знать перед обсуждением систем типов» - это здорово, излагая, почему динамическая типизация действительно отличается от статической типизации.
Питер ЛеФану Лумсдайн