Математическая аналогия класса типов это алгебраическая система. Алгебра изучает свойства объекта в
терминах операций, определённых на нём, и взаимных ограничениях этих операций. Алгебраическая систе-
ма представляет собой набор операций и свойств этих операций. Этот подход позволяет абстрагироваться
от конкретного представления объектов. Например группа – это все объекты данного типа a, для которых
определены значения: константа – единица типа a, бинарная операция типа a ->
a -> a и операция взятияобратного элемента, типа a ->
a. При этом на операции накладываются ограничения, называемые свойства-ми операций. Например, ассоциативность бинарной операции, или тот факт, что единица с любым другим
элементом, применённые к бинарной операции, дают на выходе исходный элемент.
Давайте определим класс для группы:
class Group
a wheree
::
a(+
) :: a -> a -> ainv ::
a -> aКласс с именем Group
имеет для некоторого типа a три метода: константу e :: a, операцию (+) :: a ->a ->
a и операцию взятия обратного элемента inv :: a -> a.Как и в алгебре, в Haskell классы типов позволяют описывать сущности в терминах определённых на них
операций или значений. В примерах мы указываем лишь наличие операций и их типы, так же и в классах
типов. Класс типов содержит набор имён его значений с информацией о типах значений.
Определив класс Group
, мы можем начать строить различные выражения, которые будут потом интер-претироваться специфическим для типа образом:
twice :: Group
a => a -> atwice a =
a + aisE ::
(Group a, Eq a) => a -> BoolisE 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
aclass IsPerson
a => HasName a wherename ::
a -> StringЭто определение говорит о том, что мы можем сделать экземпляр класса HasName
только для тех типов,которые содержатся в IsPerson
. Мы говорим, что класс HasName содержится в IsPerson. В этом случае классиз контекста IsPerson
называютЭто сказывается на контексте объявления типа. Теперь, если мы пишем
Классы типов | 19
fun :: HasName
a => a -> aЭто означает, что мы можем пользоваться для значений типа a как методами из класса HasName
, так иметодами из класса IsPerson
. Поскольку если тип принадлежит классу HasName, то он также принадлежит иIsPerson
.Запись (IsPerson
a => HasName a) немного обманывает, было бы точнее писать IsPerson a <= HasNamea, если тип a в классе HasName
, то он точно в классе IsPerson, но в Haskell закрепилась другая запись.1.5 Экземпляры классов типов
В
деление экземпляра пишется так же, как и определение класса типа, но вместо class
мы пишем instance,вместо некоторого типа наш конкретный тип, а вместо типов методов – уравнения для них.
Определим экземпляры для Bool
Класс Eq
:instance Eq Bool where
(==
) TrueTrue
= 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 binv a
=
not aОтметим важность наличия свойств (ограничений) у значений, определённых в классе типов. Так, на-
пример, в классе типов “сравнение на равенство” для любых двух значений данного типа одна из операций
должна вернуть “истину”, а другая “ложь”, то еесть два элемента данного типа либо равны, либо не рав-
ны. Недостаточно определить равенство для конкретного типа, необходимо убедиться в том, что для всех
элементов данного типа свойства понятия равенства не нарушаются.
На самом деле приведённое выше определение экземпляра для Group
не верно, хотя по типам оно под-ходит. Оно не верно как раз из-за нарушения свойств. Для группы необходимо, чтобы для любого a выпол-
нялось:
inv a +
a == eУ нас лишь два значения, и это свойство не выполняется ни для одного из них. Проверим:
inv True
+ True
=>
(not True) + True=> False
+ True
=>
and False