52700.fb2 Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 40

Учебник по Haskell - читать онлайн бесплатно полную версию книги . Страница 40

соответственно недопустимыми. Так например следующие значения недопустимы для Nat

Succ Zero Zero,

Succ Succ, True, Zero (Zero Succ), ...

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

ленных комбинаций слов гораздо больше, чем осмысленных предложений. Обратите внимание на то, что мы

говорим о значениях (не)допустимых для некоторого типа, например значение True допустимо для Bool, но

недопустимо для Nat.

Сами типы строятся не произвольным образом. Мы узнали, что при их построении используются две ос-

новные операции, это сумма и произведение типов. Это говорит о том, что в типах должны быть какие-то

закономерности, которые распространяются на все значения. В этой главе мы посмотрим на эти закономер-

ности.

3.1 Структура алгебраических типов данных

Итак у нас лишь две операции: сумма и произведение. Давайте для начала рассмотрим два крайних

случая.

• Только произведение типов

data T = Name T1 T2 ... TN

Мы говорим, что значение нашего нового типа T состоит из значений типов T1, T2, … , TN и у нас есть

лишь один способ составить значение этого типа. Единственное, что мы можем сделать это применить

к значениям типов Ti конструктор Name.

Пример:

data Time = Time Hour Second Minute

• Только сумма типов

data T = Name1 | Name2 | ... | NameN

40 | Глава 3: Типы

Мы говорим, что у нашего нового типа T может быть лишь несколько значений, и перечисляем их в

альтернативах через знак |.

Пример:

data Bool = True | False

Сделаем первое наблюдение: каждое произведение типов определяет новый конструктор. Число кон-

структоров в типе равно числу альтернатив. Так в первом случае у нас была одна альтернатива и следова-

тельно у нас был лишь один конструктор Name.

Имена конструкторов должны быть уникальными в пределах модуля. У нас нет таких двух типов, у ко-

торых совпадают конструкторы. Это говорит о том, что по имени конструктора компилятор знает значение

какого типа он может построить.

Произведение типов состоит из конструктора, за которым через пробел идут подтипы. Такая структура

не случайна, она копирует структуру функции. В качестве имени функции выступает конструктор, а в ка-

честве аргументов – значения заданных в произведении подтипов. Функция-конструктор после применения

“оборачивает” значения аргументов и создаёт новое значение. За счёт этого мы могли бы определить типы

по-другому. Мы могли бы определить их в стиле классов типов:

data Bool where

True

:: Bool

False :: Bool

Мы видим “класс” Bool, у которого два метода. Или определим в таком стиле Nat:

data Nat where

Zero

:: Nat

Succ

:: Nat -> Nat

Мы переписываем подтипы по порядку в аргументы метода. Или определим в таком стиле списки: