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

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