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

в декларацию типа данных, так как нам всё равно бы пришлось повторять его в функциях.

Ещё раз повторю: очень важно понимать разницу между конструкторами типов и данных. При декларации типа данных часть объявления до знака >= представляет собой конструктор типа, а часть объявления после этого знака – конструктор данных (возможны несколько конструкторов, разделённых символом >|). Попытка дать функции тип >Vector a a a -> Vector a a a -> a будет неудачной, потому что мы должны помещать типы в декларацию типа, и конструктор типа для вектора принимает только один параметр, в то время как конструктор данных принимает три. Давайте поупражняемся с нашими векторами:

>ghci> Vector 3 5 8 `vplus` Vector 9 2 8

>Vector 12 7 16

>ghci> Vector 3 5 8 `vplus` Vector 9 2 8 `vplus` Vector 0 2 3

>Vector 12 9 19

>ghci> Vector 3 9 7 `vmult` 10

>Vector 30 90 70

>ghci> Vector 4 9 5 `scalarProd` Vector 9.0 2.0 4.0

>74.0

>ghci> Vector 2 9 3 `vmult` (Vector 4 9 5 `scalarProd`

>Vector 9 2 4) Vector 148 666 222

Производные экземпляры


В разделе «Классы типов» главы 2 приводились базовые сведения о классах типов. Мы упомянули, что класс типов – это нечто вроде интерфейса, который определяет некоторое поведение. Тип может быть сделан экземпляром класса, если поддерживает это поведение. Пример: тип >Int есть экземпляр класса типов >Eq, потому что класс >Eq определяет поведение для сущностей, которые могут быть проверены на равенство. Так как целые числа можно проверить на равенство, тип >Int имеет экземпляр для класса >Eq. Реальная польза от этого видна при использовании функций, которые служат интерфейсом класса >Eq, – операторов >== и >/=. Если тип имеет определённый экземпляр класса >Eq, мы можем применять оператор >== к значениям этого типа. Вот почему выражения >4 == 4 и >"раз" /= "два" проходят проверку типов.

Классы типов часто путают с классами в языках вроде Java, Python, C++ и им подобных, что сбивает с толку множество людей. В вышеперечисленных языках классы – это нечто вроде чертежей, по которым потом создаются объекты, хранящие некое состояние и способные производить некие действия. Мы не создаём типы из классов типов – вместо этого мы сначала создаём свои типы данных, а затем думаем о том, как они могут себя вести. Если то, что мы создали, можно проверить на равенство, – определяем для него экземпляр класса >Eq. Если наш тип может вести себя как нечто, что можно упорядочить, – создаём для него экземпляр класса >Ord.

Давайте посмотрим, как язык Haskell умеет автоматически делать наши типы экземплярами таких классов типов, как