из контекста IsPersonназывают суперклассом для данного класса HasName.
Это сказывается на контексте объявления типа. Теперь, если мы пишем
Классы типов | 19
fun :: HasNamea =>a ->a
Это означает, что мы можем пользоваться для значений типа a как методами из класса HasName, так и
методами из класса IsPerson. Поскольку если тип принадлежит классу HasName, то он также принадлежит и
IsPerson.
Запись ( IsPersona => HasNamea) немного обманывает, было бы точнее писать IsPersona <= HasName
a, если тип a в классе HasName, то он точно в классе IsPerson, но в Haskell закрепилась другая запись.
1.5 Экземпляры классов типов
В экземплярах (instance) классов типов мы даём конкретное наполнение для методов класса типов. Опре-
деление экземпляра пишется так же, как и определение класса типа, но вместо classмы пишем instance,
вместо некоторого типа наш конкретный тип, а вместо типов методов – уравнения для них.
Определим экземпляры для 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
• Комбинации суммы и произведения типов:
Читать дальше