Россия, Петерубрг, СПБ-ГПУ, 1998 |
Объявления и связывания имен
4.1.1 Виды
Для того чтобы гарантировать их допустимость, выражения с типами разделены на классы различных видов . Каждый вид имеет одну из двух возможных форм:
- Символ * представляет вид, к которому относятся все конструкторы типов с нулевым числом аргументов.
- Если 1 и 2 являются видами, то 1 -> 2 - вид, к которому относятся типы, у которых тип принимаего аргумента относится к виду 1, а тип возвращаемого значения - к виду 2.
Правильность выражений с типами проверяется с помощью вывода вида подобно тому, как правильность выражений со значениями проверяется с помощью вывода типа. Однако, в отличие от типов, виды являются полностью неявными и не являются видимой частью языка. Вывод видов рассматривается в разделе "Объявления и связывания имен" .
4.1.2. Синтаксис типов
type | btype [-> type] | (тип функции) | |
btype | [btype] atype | ||
atype | gtycon | ||
| | tyvar | ||
| | ( type1 , … , typek ) | (тип кортежа, k >= 2 ) | |
| | [ type ] | (тип списка) | |
| | ( type ) | (конструктор в скобках) | |
gtycon | qtycon | ||
| | () | (тип объединения) | |
| | [] | (конструктор списка) | |
| | ( ) | (конструктор функции) | |
| | (,{,}) | (конструкторы кортежей) |
Перевод:
тип | b-тип [-> тип] | (тип функции) | |
b-тип | [b-тип] a-тип | (наложение типов) | |
a-тип | общий-конструктор-типа | ||
| | переменная-типа | ||
| | ( тип1 , … , типk ) | (тип кортежа, k >= 2 ) | |
| | [ тип ] | (тип списка) | |
| | ( тип ) | (конструктор в скобках) | |
общий-конструктор-типа | квалифицированный-конструктор-типа | ||
| | () | (тип объединения) | |
| | [] | (конструктор списка) | |
| | ( ) | (конструктор функции) | |
| | (,{,}) | (конструкторы кортежей) |
Синтаксис для выражений с типами в Haskell описан выше. Подобно тому, как значения данных построены с использованием конструкторов данных, значения типов построены из конструкторов типов. Как и с конструкторами данных, имена конструкторов типов начинаются с заглавных букв. В отличие от конструкторов данных, инфиксные конструкторы типов не допускаются (отличные от ( )).
Основными видами выражений с типами являются следующие:
- Переменные типов, которые обозначаются идентификаторами, начинающимися со строчной буквы. Вид, к которому относится переменная, определяется неявно из контекста, в котором она появилась.
-
Конструкторы типов. Большинство конструкторов типов обозначаются идентификаторами, начинающимися с заглавной буквы. Например:
- Char, Int, Integer, Float, Double и Bool являются константами типов и относятся к виду *.
- Maybe и IO являются конструкторами типов с одним аргументом и рассматриваются как типы, относящиеся к виду * *.
- Объявления data T … или newtype T … добавляют в список типов конструктор типа T. Вид, к которому относится тип T, определяется с помощью вывода вида.
Для конструкторов определенных встроенных типов предусмотрен специальный синтаксис:
- Тривиальный тип обозначается () и относится к виду *. Он обозначает тип "кортеж с нулевым числом аргументов" и имеет ровно одно значение, которое также обозначается () (см. разделы "Выражения" и "Предопределенные типы и классы" ).
- Тип функции обозначается (-") и относится к виду * * *.
- Тип списка обозначается [] и относится к виду * *.
- Типы кортежей обозначаются (,), (,,) и так далее. Они относятся к видам * * *, * * * *> и так далее .
Использование констант ( ) и [] более подробно описано ниже.
- Наложение типов. Если t1 - тип, относящийся к виду 1 2, а t2 - тип, относящийся к виду 1, то t1 t2 является выражением с типом, относящимся к виду 2.
- Тип в скобках вида (t) идентичен типу t.
Например, выражение типа IO a можно воспринимать как применение константы IO к переменной a. Поскольку конструктор типа IO относится к виду * *, из этого следует, что и переменная a, и все выражение IO a должно относиться к виду *. Вообще, процесс вывода вида (см. раздел "Объявления и связывания имен" ) необходим для того, чтобы установить соответствующие виды для определяемых пользователем типов данных, синонимов типов и классов.
Поддерживается специальный синтаксис, который позволяет записывать выражения с определенными типами с использованием более традиционного стиля:
- Тип функции имеет вид t1 -> t2 и эквивалентен типу (->) t1 t2. Стрелки функций являются правоассоциативными операциями. Например, Int -> Int -> Float означает Int -> (Int -> Float).
- Тип кортежа имеет вид (t1, … , tk), где k >= 2, и эквивалентен типу (,…,) t1 … tk, где имеются k-1 запятых между круглыми скобками. Он обозначает тип k -кортежей, у которых первая компонента имеет тип t1, вторая компонента - тип t2 и так далее (см. разделы "Выражения" и "Предопределенные типы и классы" ).
- Тип списка имеет вид [t] и эквивалентен типу [] t. Он обозначает тип списков с элементами типа t (см. разделы "Выражения" и "Предопределенные типы и классы" ).
Эти специальные синтаксические формы всегда обозначают конструкторы встроенных типов для функций, кортежей и списков, независимо от того, что находится в области видимости. Аналогично, префиксные конструкторы типов (->), [], (), (,) и так далее всегда обозначают конструкторы встроенных типов; их нельзя ни использовать с квалификаторами, ни указывать в списках импорта или экспорта (лекция "Модули" ). (Отсюда специальное правило вывода gtycon (общего-конструктора-типа), описанное выше.)
Несмотря на то, что у типов списков и кортежей специальный синтаксис, их семантика - такая же, как и у эквивалентных определяемых пользователем алгебраических типов данных.
Отметим, что выражения и типы имеют согласующийся синтаксис. Если ti - тип выражения или образца ei, то выражения (\ e1 -> e2), [e1] и (e1,e2) имеют соответственно типы (t1 -> t2), [t1] и (t1, t2).
За одним исключением (переменной типа в объявлении класса (раздел "Объявления и связывания имен" ), все переменные типов в выражении с типами Haskell предполагаются стоящими под квантором всеобщности; квантор всеобщности [3] не указывается явно, для этого нет специального синтаксиса. Например, выражение a -> a обозначает тип forall a. A -> a. Тем не менее, для ясности, мы часто записываем кванторы явно при обсуждении типов программ на Haskell . Когда мы записываем тип с явным использованием квантора, область действия квантора forall (для всех) простирается вправо насколько возможно, например, forall a. A -> a означает forall a. (a -> a).
4.1.3. Синтаксис утверждений классов и контекстов
context | class | ||
| | ( class1 , ... , classn ) | (n >=0) | |
class | qtycls tyvar | ||
| | qtycls ( tyvar atype1 ... atypen ) | (n >= 1) | |
qtycls | [ modid . ] tycls | ||
tycls | conid | ||
tyvar | varid |
Перевод:
контекст | класс | (n >= 0) | |
| | ( класс1 , ... , классn ) | ||
класс | квалифицированный-класс-типа переменная-типа | ||
| | квалифицированный-класс-типа ( переменная-типа a-тип1 ... a-типn ) | (n >= 1) | |
квалифицированный-класс-типа | [ идентификатор-модуля . ] класс-типа | ||
класс-типа | идентификатор-конструктора | ||
переменная-типа | идентификатор-переменной |
Утверждение класса имеет вид qtycls tyvar и указывает на то, что тип tyvar является элементом класса qtycls. Идентификатор класса начинается с заглавной буквы. Контекст состоит из нуля или более утверждений класса и имеет общий вид
( C1 u1, ..., Cn un )
где C1, ..., Cn - идентификаторы класса, и каждый из u1, ..., un является или переменной типа, или применением переменной типа к одному или более типам. Внешние круглые скобки можно опустить при n=1. Вообще, мы используем cx для обозначения контекста и мы записываем cx => t для указания того, что тип t ограничен контекстом cx. Контекст cx должен содержать только переменные типов, упомянутые в t. Для удобства мы записываем cx => t, даже если контекст cx пуст, хотя в этом случае конкретный синтаксис не содержит