Производные экземпляры
В разделе «Классы типов» главы 2 приводились базовые сведения о классах типов. Мы упомянули, что класс типов – это нечто вроде интерфейса, который определяет некоторое поведение. Тип может быть сделан экземпляром класса, если поддерживает это поведение. Пример: тип Int есть экземпляр класса типов Eq, потому что класс Eq определяет поведение для сущностей, которые могут быть проверены на равенство. Так как целые числа можно проверить на равенство, тип Int имеет экземпляр для класса Eq. Реальная польза от этого видна при использовании функций, которые служат интерфейсом класса Eq, – операторов == и /=. Если тип имеет определённый экземпляр класса Eq, мы можем применять оператор == к значениям этого типа. Вот почему выражения 4 == 4 и "раз" /= "два" проходят проверку типов.
Классы типов часто путают с классами в языках вроде Java, Python, C++ и им подобных, что сбивает с толку множество людей. В вышеперечисленных языках классы – это нечто вроде чертежей, по которым потом создаются объекты, хранящие некое состояние и способные производить некие действия. Мы не создаём типы из классов типов – вместо этого мы сначала создаём свои типы данных, а затем думаем о том, как они могут себя вести. Если то, что мы создали, можно проверить на равенство, – определяем для него экземпляр класса Eq. Если наш тип может вести себя как нечто, что можно упорядочить, – создаём для него экземпляр класса Ord.
Давайте посмотрим, как язык Haskell умеет автоматически делать наши типы экземплярами таких классов типов, как Eq, Ord, Enum, Bounded, Show и Read. Haskell умеет порождать поведение для наших типов в этих контекстах, если мы используем ключевое слово deriving при создании типа данных.