Выбрать главу

вместо некоторого типа наш конкретный тип, а вместо типов методов – уравнения для них.

Определим экземпляры для Bool

Класс Eq:

instance Eq Bool where

(==) True

True

= True

(==) False False = True

(==) _

_

= False

(/=) a b

= not (a == b)

Класс Show:

instance Show Bool where

show True

= ”True”

show False = ”False”

Класс Group:

instance Group Bool where

e

= True

(+) a b = and a b

inv a

= not a

Отметим важность наличия свойств (ограничений) у значений, определённых в классе типов. Так, на-

пример, в классе типов “сравнение на равенство” для любых двух значений данного типа одна из операций

должна вернуть “истину”, а другая “ложь”, то еесть два элемента данного типа либо равны, либо не рав-

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

элементов данного типа свойства понятия равенства не нарушаются.

На самом деле приведённое выше определение экземпляра для Group не верно, хотя по типам оно под-

ходит. Оно не верно как раз из-за нарушения свойств. Для группы необходимо, чтобы для любого a выпол-

нялось:

inv a + a == e

У нас лишь два значения, и это свойство не выполняется ни для одного из них. Проверим:

inv True

+ True

=> (not True) + True

=> False

+ True

=> and False

True

=> False

inv False

+ False

=> (not False) + False

=> True

+ False

=> and True

False

=> False

Проверять свойства очень важно, потому что другие люди, читая ваш код и используя ваши функции,

будут на них рассчитывать.

20 | Глава 1: Основы

1.6 Ядро Haskell

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

вспомним синтаксические конструкции, которые нам встретились.

Модули

module New(edef1, edef2, ... , edefN) where

import Old1(idef11, idef12, ... , idef1N)

import Old2(idef21, idef22, ... , idef2M)

...

import OldK(idefK1, idefK2, ... , idefKP)

-- определения :

...

Ключевые слова: module, where, import. Мы определили модуль с именем New, который экспортирует

определения edef1, edef2, … , edefN. И импортирует определения из модулей Old1, Old2, и т.д., определения

написаны в скобках за ключевыми словами import и именами модулей.

Типы

Тип определяется с помощью:

• Перечисления альтернатив через |

data Type = Alt1 | Alt2 | ... | AltN

Эту операцию называют суммой типов.

• Составления сложного типа из подтипов, пишем конструктор первым, затем через пробел подтипы:

data Type = Name

Sub1

Sub2

...

SubN

Эту операцию называют произведением типов.

Есть одно исключение: если тип состоит из двух подтипов, мы можем дать конструктору символьное

(а не буквенное) имя, но оно должно начинаться с двоеточия :, как в случае списка, например, можно

делать такие определения типов:

data Type = Sub1 :+ Sub2

data Type = Sub1 :| Sub2

• Комбинации суммы и произведения типов:

data Type = Name1

Sub11

Sub12

...

Sub1N

| Name2

Sub21

Sub22

...

Sub2M

...

| NameK

SubK1

SubK2

...

SubKP

Такие типы называют алгебраическими типами данных. С помощью типов мы определяем основные поня-

тия и способы их комбинирования.

Значения

Как это ни странно, нам встретилась лишь одна операция создания значений: определение синонима. Она

пишется так

name x1

x2 ... xN = Expr1

name x1

x2 ... xN = Expr2

name x1

x2 ... xN = Expr3

Слева от знака равно стоит составное имя, а справа от знака равно некоторое выражение, построенное

согласно типам. Разные комбинации имени name с параметрами определяют разные уравнения для синонима

name.

Также мы видели символ _, который означает “всё, что угодно” на месте аргумента. А также мы увидели,