Опубликован: 19.09.2008 | Доступ: свободный | Студентов: 658 / 70 | Оценка: 4.50 / 5.00 | Длительность: 21:25:00
Лекция 5:

Объявления и связывания имен

4.1.1 Виды

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

  • Символ * представляет вид, к которому относятся все конструкторы типов с нулевым числом аргументов.
  • Если 1 и 2 являются видами, то 1 -> 2 - вид, к которому относятся типы, у которых тип принимаего аргумента относится к виду 1, а тип возвращаемого значения - к виду 2.

Правильность выражений с типами проверяется с помощью вывода вида подобно тому, как правильность выражений со значениями проверяется с помощью вывода типа. Однако, в отличие от типов, виды являются полностью неявными и не являются видимой частью языка. Вывод видов рассматривается в разделе "Объявления и связывания имен" .

4.1.2. Синтаксис типов

type \to btype [-> type] (тип функции)
btype \to [btype] atype
atype \to gtycon
| tyvar
| ( type1 , … , typek ) (тип кортежа, k >= 2 )
| [ type ] (тип списка)
| ( type ) (конструктор в скобках)
gtycon \to qtycon
| () (тип объединения)
| [] (конструктор списка)
| ( \to ) (конструктор функции)
| (,{,}) (конструкторы кортежей)

Перевод:

тип \to b-тип [-> тип] (тип функции)
b-тип \to [b-тип] a-тип (наложение типов)
a-тип \to общий-конструктор-типа
| переменная-типа
| ( тип1 , … , типk ) (тип кортежа, k >= 2 )
| [ тип ] (тип списка)
| ( тип ) (конструктор в скобках)
общий-конструктор-типа \to квалифицированный-конструктор-типа
| () (тип объединения)
| [] (конструктор списка)
| ( \to ) (конструктор функции)
| (,{,}) (конструкторы кортежей)

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

Основными видами выражений с типами являются следующие:

  1. Переменные типов, которые обозначаются идентификаторами, начинающимися со строчной буквы. Вид, к которому относится переменная, определяется неявно из контекста, в котором она появилась.
  2. Конструкторы типов. Большинство конструкторов типов обозначаются идентификаторами, начинающимися с заглавной буквы. Например:
    • Char, Int, Integer, Float, Double и Bool являются константами типов и относятся к виду *.
    • Maybe и IO являются конструкторами типов с одним аргументом и рассматриваются как типы, относящиеся к виду * \to *.
    • Объявления data T … или newtype T … добавляют в список типов конструктор типа T. Вид, к которому относится тип T, определяется с помощью вывода вида.

    Для конструкторов определенных встроенных типов предусмотрен специальный синтаксис:

    • Тривиальный тип обозначается () и относится к виду *. Он обозначает тип "кортеж с нулевым числом аргументов" и имеет ровно одно значение, которое также обозначается () (см. разделы "Выражения" и "Предопределенные типы и классы" ).
    • Тип функции обозначается (-") и относится к виду * \to * \to *.
    • Тип списка обозначается [] и относится к виду * \to *.
    • Типы кортежей обозначаются (,), (,,) и так далее. Они относятся к видам * \to * \to *, * \to * \to * \to *> и так далее .

    Использование констант ( \to ) и [] более подробно описано ниже.

  3. Наложение типов. Если t1 - тип, относящийся к виду 1 \to 2, а t2 - тип, относящийся к виду 1, то t1 t2 является выражением с типом, относящимся к виду 2.
  4. Тип в скобках вида (t) идентичен типу t.

Например, выражение типа IO a можно воспринимать как применение константы IO к переменной a. Поскольку конструктор типа IO относится к виду * \to *, из этого следует, что и переменная a, и все выражение IO a должно относиться к виду *. Вообще, процесс вывода вида (см. раздел "Объявления и связывания имен" ) необходим для того, чтобы установить соответствующие виды для определяемых пользователем типов данных, синонимов типов и классов.

Поддерживается специальный синтаксис, который позволяет записывать выражения с определенными типами с использованием более традиционного стиля:

  1. Тип функции имеет вид t1 -> t2 и эквивалентен типу (->) t1 t2. Стрелки функций являются правоассоциативными операциями. Например, Int -> Int -> Float означает Int -> (Int -> Float).
  2. Тип кортежа имеет вид (t1, … , tk), где k >= 2, и эквивалентен типу (,…,) t1 … tk, где имеются k-1 запятых между круглыми скобками. Он обозначает тип k -кортежей, у которых первая компонента имеет тип t1, вторая компонента - тип t2 и так далее (см. разделы "Выражения" и "Предопределенные типы и классы" ).
  3. Тип списка имеет вид [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 \to class
| ( class1 , ... , classn ) (n >=0)
class \to qtycls tyvar
| qtycls ( tyvar atype1 ... atypen ) (n >= 1)
qtycls \to [ modid . ] tycls
tycls \to conid
tyvar \to varid

Перевод:

контекст \to класс (n >= 0)
| ( класс1 , ... , классn )
класс \to квалифицированный-класс-типа переменная-типа
| квалифицированный-класс-типа ( переменная-типа a-тип1 ... a-типn ) (n >= 1)
квалифицированный-класс-типа \to [ идентификатор-модуля . ] класс-типа
класс-типа \to идентификатор-конструктора
переменная-типа \to идентификатор-переменной

Утверждение класса имеет вид qtycls tyvar и указывает на то, что тип tyvar является элементом класса qtycls. Идентификатор класса начинается с заглавной буквы. Контекст состоит из нуля или более утверждений класса и имеет общий вид

( C1 u1, ..., Cn un )

где C1, ..., Cn - идентификаторы класса, и каждый из u1, ..., un является или переменной типа, или применением переменной типа к одному или более типам. Внешние круглые скобки можно опустить при n=1. Вообще, мы используем cx для обозначения контекста и мы записываем cx => t для указания того, что тип t ограничен контекстом cx. Контекст cx должен содержать только переменные типов, упомянутые в t. Для удобства мы записываем cx => t, даже если контекст cx пуст, хотя в этом случае конкретный синтаксис не содержит \Rightarrow.