Переменная типаПеременная типа (ти́повая переменная) в языках программирования и теории типов — переменная, которая может принимать значение из множества типов данных. Ти́повая переменная используется в определении алгебраического типа данных подобно тому, как используется параметр в определении функции, но используется для передачи типа данных без передачи самих данных. В качестве идентификаторов ти́повых переменных в теории типов традиционно используются буквы греческого алфавита (хотя многие языки программирования используют латиницу и допускают и более длинные именования). ПримерВ следующем определении полиморфного типа «список» на языке Standard ML, идентификатор datatype 'a list = nil | :: of 'a * 'a list
При использовании этого полиморфного типа, в ти́повую переменную подставляется конкретный тип, так что в программе может быть сформировано множество мономорфных типов: Если полиморфный тип параметризован несколькими переменными типа, то подставляемые в них типы могут быть как разными, так и идентичными, но правило подстановки соблюдается. Например, если такой тип: datatype ('a,'b) t = Single of 'a | Double of 'a * 'a | Pair of 'a * 'b
инстанцировать следующим образом: type t_ir = (int, real) t
то Связывание и квантификацияОбласть действия всякой ти́повой переменной привязывается к определённому контексту[3][4]. В следующем примере идентификатор val foo: 'a -> 'a
val bar: 'a -> 'a
Это становится наглядным при использовании явного связывания (explicit scoping или explicit bounding) ти́повой переменной: val foo: ['a] 'a -> 'a
val bar: ['a] 'a -> 'a
Связывание ограничивает область действия данной переменной типа. В классических диалектах ML явное связывание ти́повых переменных является необязательной возможностью и большинством реализаций не поддерживается за ненадобностью — связывание в них осуществляется неявно при выведении типов, что становится возможным за счёт ограничений ранних версий системы Хиндли — Милнера. В полной системе F это объявление записывается как . Такая запись называется пренексной нормальной формой[англ.]. Заглавная в этой записи означает функцию слоя типов (type-level function), параметром которой и является переменная типа. После подстановки в эту переменную конкретного типа, эта функция возвращает мономорфную функцию слоя значений (value-level function). Пренексом называется вынесенное в качестве префикса к сигнатуре типа явное связывание переменной типа. Ранние версии системы Хиндли — Милнера разрешают только пренексную форму, то есть требуют, чтобы инстанцирование ти́повой переменной определённым значением было произведено до необходимости обращения к функции. Это ограничение называется пренексным полиморфизмом — оно не только существенно упрощает механизм проверки согласования типов, но и делает возможным выведение всех или почти всех (в зависимости от диалекта) типов в программе. Простейшее связывание ти́повых переменных называется их квантификацией. Выделяются два случая:
Далеко не во всех случаях «превращение» универсально полиморфного типа в экзистенциальный даёт применимый на практике тип или заметные отличия от универсального полиморфизма, но в определённых случаях использование экзистенциальных типов поднимает параметрический полиморфизм на первоклассный уровень, т.е. позволяет передавать полиморфные функции без связывания в качестве параметров другим функциям (см. полиморфизм первого класса). Классическим примером является реализация гетерогенного списка (см. викиучебник). Явное аннотирование типов в этом случае становится обязательным, т.к. выведение типов для полиморфизма выше ранга 2 алгоритмически неразрешимо[5]. На практике, универсально полиморфные типы дают обобщённость типа данных, а экзистенциальные — абстрактность типа данных[6]. В языке Haskell различают несколько режимов (доступных в виде расширений языка): режим Rank2Types разрешает лишь некоторые формы экзистенциальных типов, открывающие полиморфизм не выше 2-го ранга, для которого выведение типов ещё разрешимо; режим RankNTypes разрешает перемещать универсальный квантор ( OCaml реализует поддержку экзистенциальной квантификации посредством решения, названного «локально-абстрактными типами» (locally abstract types)[9]. В спецификации Standard ML для некоторых встроенных операций определена особая квантификация. Её синтаксис не регламентирован и различается в реализациях языка (чаще всего просто скрывается). Например, сигнатура встроенной операции сложения может упрощённо выглядеть примерно следующим образом: val + : [int, word, real] 'a * 'a -> 'a
Эта семантика реализует примитивную форму ad-hoc-полиморфизма — объединение нескольких физически различных операций сложения под единым идентификатором « В конце 1980-х появилось расширение Хиндли — Милнера, предоставляющее возможность ограничивать по необходимости диапазон значений для всякой ти́повой переменной во вновь определяемых типах — классы типов. Класс типов строже ограничивает допустимые контексты типизации, разрешая инстанцирование ти́повой переменной лишь типами, принадлежащими явно указанному классу. Впервые это расширение было реализовано в основе языка Haskell, например, операция сравнения на равенство имеет в нём следующую сигнатуру[англ.]: (==) :: (Eq a) => a -> a -> Bool
Проект языка следующего поколения — successor ML[1] — отказывается от необходимости выведения типов, опираясь на явное (манифестное) аннотирование типов (в том числе явное связывание переменных типа), что обеспечивает непосредственную поддержку полной системы F с полиморфизмом первого класса и рядом расширений, в том числе иерархии подтипов[англ.] и экзистенциальных типов[1]. Специальные переменные типаОсновным классом переменных типа, используемым во всех языках семейства ML, являются аппликативные переменные типа, могут принимать значения из множества всех допустимых в конкретном языке типов. В классических диалектах они синтаксически обозначаются как « Кроме этого, на протяжении истории развития ML выделялись специальные подклассы переменных типа. Переменные типа, допускающего проверку на равенство (или переменные сравниваемого типа, equality type variables) — могут принимать значения из множества всех типов, допускающих проверку на равенство (англ. equality types). Их использование разрешает применение операции сравнения на равенство к объектам полиморфных типов. Обозначаются как « Императивные переменные типа (imperative type variables) обеспечивали ситуативное решение проблемы типобезопасности, связанной с наличием побочных эффектов в языке с параметрическим полиморфизмом. Этой проблемы не возникает в чистых языках (Haskell, Clean), но для нечистых языков (Standard ML, OCaml) полиморфизм ссылок представляет угрозу ошибок типизации[3][4]. Императивные переменные типа входили в Определение SML’90, но перестали иметь собственный смысл после того как было придумано «ограничение значениями» (value restriction), ставшее частью пересмотренного определения SML’97. Синтаксическая поддержка императивных переменных типа в SML’97 была сохранена для обратной совместимости с написанным ранее кодом, но современные реализации трактуют их идентично аппликативным. Обозначаются « Слабые переменные типа (weak type variables) использовались в компиляторе SML/NJ[англ.] в качестве альтернативы императивным переменным типа, предоставляя существенно большие возможности (точнее, меньшие ограничения). Обозначаются « Примечания
Литература
Ссылки
|