, но можем – функцию типа:
>a –> Maybe a
или
>Maybe Int –> Maybe String
). Вот почему недопустимо делать что-нибудь в таком роде:
>instance Eq Maybe where
> ...
Ведь, как мы видели, идентификатор >a
должен принимать значение в виде конкретного типа, а тип >Maybe
не является таковым. Это конструктор типа, который принимает один параметр и производит конкретный тип.
Было бы скучно прописывать >instance Eq (Maybe Int) where
, >instance Eq (Maybe Char) where
и т. д. для всех существующих типов. Вот почему мы можем записать это так:
>instance Eq (Maybe m) where
> Just x == Just y = x == y
> Nothing == Nothing = True
> _ == _ = False
Это всё равно что сказать, что мы хотим сделать для всех типов формата >Maybe <нечто>
экземпляр класса >Eq
. Мы даже могли бы записать >(Maybe something)
, но обычно программисты используют одиночные буквы, чтобы придерживаться стиля языка Haskell. Выражение >(Maybe m)
выступает в качестве типа >a
в декларации >class Eq a where
. Тип >Maybe
не является конкретным типом, а >Maybe m
– является. Указание типа-параметра (>m
в нижнем регистре) свидетельствует о том, что мы хотим, чтобы все типы вида >Maybe
>m
, где >m
– любой тип, имели экземпляры класса >Eq
.
Однако здесь есть одна проблема. Заметили? Мы используем оператор >==
для содержимого типа >Maybe
, но у нас нет уверенности, что то, что содержит тип >Maybe
, может быть использовано с методами класса >Eq
. Вот почему необходимо поменять декларацию экземпляра на следующую:
>instance (Eq m) => Eq (Maybe m) where
> Just x == Just y = x == y
> Nothing == Nothing = True
> _ == _ = False
Нам пришлось добавить ограничение на класс. Таким объявлением экземпляра класса мы утверждаем: необходимо, чтобы все типы вида >Maybe
>m
имели экземпляр для класса >Eq
, но при этом тип >m
(тот, что хранится в >Maybe
) также должен иметь экземпляр класса >Eq
. Такой же экземпляр породил бы сам язык Haskell, если бы мы воспользовались директивой >deriving
.
В большинстве случаев ограничения на класс в декларации класса используются для того, чтобы сделать класс подклассом другого класса. Ограничения на класс в определении экземпляра используются для того, чтобы выразить требования к содержимому некоторого типа. Например, в данном случае мы требуем, чтобы содержимое типа >Maybe
также имело экземпляр для класса >Eq
.
При создании экземпляров, если вы видите, что тип использовался как конкретный при декларации (например, >a –> a –> Bool
), а вы реализуете экземпляр для конструктора типов, следует предоставить тип-параметр и добавить скобки, чтобы получить конкретный тип.