Объявления и связывания имен
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 пуст, хотя в этом случае конкретный синтаксис не содержит 