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

Программистская аналогия класса типов это интерфейс. В интерфейсе определён набор значений (как

констант, так и функций), которые могут быть применены ко всем типам, которые поддерживают данный

интерфейс. К примеру, в интерфейсе “сравнение на равенство” для некоторого типа a определены две функ-

ции: равно (==) и не равно (/=) с одинаковым типом a -> a -> Bool, или в интерфейсе “печати” для любого

типа a определена одна функция show типа a -> String.

Математическая аналогия класса типов это алгебраическая система. Алгебра изучает свойства объекта в

терминах операций, определённых на нём, и взаимных ограничениях этих операций. Алгебраическая систе-

ма представляет собой набор операций и свойств этих операций. Этот подход позволяет абстрагироваться

от конкретного представления объектов. Например группа – это все объекты данного типа a, для которых

определены значения: константа – единица типа a, бинарная операция типа a -> a -> a и операция взятия

обратного элемента, типа a -> a. При этом на операции накладываются ограничения, называемые свойства-

ми операций. Например, ассоциативность бинарной операции, или тот факт, что единица с любым другим

элементом, применённые к бинарной операции, дают на выходе исходный элемент.

Давайте определим класс для группы:

class Group a where

e

:: a

(+) :: a -> a -> a

inv :: a -> a

Класс с именем Group имеет для некоторого типа a три метода: константу e :: a, операцию (+) :: a ->

a -> a и операцию взятия обратного элемента inv :: a -> a.

Как и в алгебре, в Haskell классы типов позволяют описывать сущности в терминах определённых на них

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

типов. Класс типов содержит набор имён его значений с информацией о типах значений.

Определив класс Group, мы можем начать строить различные выражения, которые будут потом интер-

претироваться специфическим для типа образом:

twice :: Group a => a -> a

twice a = a + a

isE :: (Group a, Eq a) => a -> Bool

isE x = (x == e)

Обратите внимание на запись Group a => и (Group a, Eq a) => . Это называется контекстом объявления

типа. В контексте мы говорим, что данный тип должен быть из класса Group или из классов Group и Eq. Это

значит, что для этого типа мы можем пользоваться методами из этих классов.

В первой функции twice мы воспользовались методом (+) из класса Group, поэтому функция имеет кон-

текст Group a => . А во второй функции isE мы воспользовались методом e из класса Group и методом (==)

из класса Eq, поэтому функция имеет контекст (Group a, Eq a) => .

Контекст классов типов. Суперклассы

Класс типов также может содержать контекст. Он указывается между словом class и именем класса.

Например

class IsPerson a

class IsPerson a => HasName a where

name :: a -> String

Это определение говорит о том, что мы можем сделать экземпляр класса HasName только для тех типов,

которые содержатся в IsPerson. Мы говорим, что класс HasName содержится в IsPerson. В этом случае класс

из контекста IsPerson называют суперклассом для данного класса HasName.

Это сказывается на контексте объявления типа. Теперь, если мы пишем

Классы типов | 19

fun :: HasName a => a -> a

Это означает, что мы можем пользоваться для значений типа a как методами из класса HasName, так и

методами из класса IsPerson. Поскольку если тип принадлежит классу HasName, то он также принадлежит и

IsPerson.

Запись (IsPerson a => HasName a) немного обманывает, было бы точнее писать IsPerson a <= HasName

a, если тип a в классе HasName, то он точно в классе IsPerson, но в Haskell закрепилась другая запись.

1.5 Экземпляры классов типов

В экземплярах (instance) классов типов мы даём конкретное наполнение для методов класса типов. Опре-

деление экземпляра пишется так же, как и определение класса типа, но вместо class мы пишем instance,