, мы должны реализовать функцию
>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
используется как конкретный тип, потому что все типы в функциях должны быть конкретными (помните, мы обсуждали, что не можем иметь функцию типа