Изучай Haskell во имя добра! (Липовача) - страница 90

, >Ord, >Enum, >Bounded, >Show и >Read. Haskell умеет порождать поведение для наших типов в этих контекстах, если мы используем ключевое слово >deriving при создании типа данных.

Сравнение людей на равенство

Рассмотрим такой тип данных:

>data Person = Person { firstName :: String

>                     , lastName :: String

>                     , age :: Int

>                     }

Тип описывает человека. Предположим, что среди людей не встречаются тёзки одного возраста. Если у нас есть два описания, можем ли мы выяснить, относятся ли они к одному и тому же человеку? Есть ли в такой операции смысл? Конечно, есть. Мы можем сравнить записи и проверить, равны они или нет. Вот почему имело бы смысл определить для нашего типа экземпляр класса >Eq. Порождаем экземпляр:

>data Person = Person { firstName :: String

>                     , lastName :: String

>                     , age :: Int

>                     } deriving (Eq)

Когда мы определяем экземпляр класса >Eq для типа и пытаемся сравнить два значения с помощью операторов >== или >/=, язык Haskell проверяет, совпадают ли конструкторы значений (хотя в нашем типе только один конструктор), а затем проверяет все данные внутри конструктора на равенство, сравнивая каждую пару полей с помощью оператора >==. Таким образом, типы всех полей также должны иметь определённый экземпляр класса >Eq. Так как типы полей нашего типа, >String и >Int, имеют экземпляры класса >Eq, всё в порядке.

Запишем в файл несколько людей:

>mikeD = Person {firstName = "Майкл", lastName = "Даймонд", age = 45}

>adRock = Person {firstName = "Адам", lastName = "Горовиц", age = 45}

>mca = Person {firstName = "Адам", lastName = "Яух", age = 47}

И проверим экземпляр класса >Eq:

>ghci> mca == adRock

>False

>ghci> mikeD == adRock

>False

>ghci> mikeD == mikeD

>True

>ghci> mca == Person {firstName = "Адам", lastName = "Яух", age = 47}

>True

Конечно же, так как теперь тип >Person имеет экземпляр класса >Eq, мы можем передавать его любым функциям, которые содержат ограничение на класс типа >Eq в декларации, например функции >elem.

>ghci> let beastieBoys = [mca, adRock, mikeD]

>ghci> mikeD `elem` beastieBoys

>True

Покажи мне, как читать

Классы типов >Show и >Read предназначены для сущностей, которые могут быть преобразованы в строки и из строк соответственно. Как и для класса >Eq, все типы в конструкторе типов также должны иметь экземпляры для классов >Show и/или >Read, если мы хотим получить такое поведение. Давайте сделаем наш тип данных >Person частью классов >Show и >Read:

>data Person = Person { firstName :: String

>                     , lastName :: String