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

, мы должны реализовать функцию >show, которая принимает значение и возвращает строку:

>instance Show TrafficLight where

>   show Red = "Красный свет"

>   show Yellow = "Жёлтый свет"

>   show Green = "Зелёный свет"

Мы снова использовали сопоставление с образцом, чтобы достичь нашей цели. Давайте посмотрим, как это всё работает:

>ghci> Red == Red

>True

>ghci> Red == Yellow

>False

>ghci> Red `elem` [Red, Yellow, Green]

>True

>ghci> [Red, Yellow, Green]

>[Красный свет,Жёлтый свет,Зелёный свет]

Можно было бы просто автоматически сгенерировать экземпляр для класса >Eq с абсолютно тем же результатом (мы этого не сделали в образовательных целях). Кроме того, автоматическая генерация для класса >Show просто напрямую переводила бы конструкторы значений в строки. Если нам требуется печатать что-то дополнительно, то придётся создавать экземпляр класса >Show вручную.

Наследование классов

Также можно создавать классы типов, которые являются подклассами других классов типов. Декларация класса >Num довольно длинна, но вот её начало:

>class (Eq a) => Num a where

>   ...

Как уже говорилось ранее, есть множество мест, куда мы можем втиснуть ограничения на класс. Наша запись равнозначна записи >class Num a where, но мы требуем, чтобы тип >a имел экземпляр класса >Eq. Это означает, что мы должны определить для нашего типа экземпляр класса >Eq до того, как сможем сделать для него экземпляр класса >Num. Прежде чем некоторый тип сможет рассматриваться как число, мы должны иметь возможность проверять значения этого типа на равенство.

Ну вот и всё, что надо знать про наследование, – это просто ограничения на класс типа-параметра при объявлении класса. При написании тел функций в декларации класса или при их определении в экземпляре класса мы можем полагать, что тип >a имеет экземпляр для класса >Eq и, следовательно, допускается использование операторов >== и >/= со значениями этого типа.

Создание экземпляров классов для параметризованных типов

Но как тип >Maybe и списковый тип сделаны экземплярами классов? Тип >Maybe отличается, скажем, от типа >TrafficLight тем, что >Maybe сам по себе не является конкретным типом – это конструктор типов, который принимает один тип-параметр (например, >Char), чтобы создать конкретный тип (как >Maybe Char). Давайте посмотрим на класс >Eq ещё раз:

>class Eq a where

>   (==) :: a –> a –> Bool

>   (/=) :: a –> a –> Bool

>   x == y = not (x /= y)

>   x /= y = not (x == y)

Из декларации типа мы видим, что >a используется как конкретный тип, потому что все типы в функциях должны быть конкретными (помните, мы обсуждали, что не можем иметь функцию типа