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

из модуля >Data.Map. Параметр >k – это тип ключей в отображении, параметр >v – тип значений. Это отличный пример правильного использования параметризации типов. Параметризация отображений позволяет нам использовать любые типы, требуя лишь, чтобы тип ключа имел экземпляр класса >Ord. Если бы мы определяли тип для отображений, то могли бы добавить ограничение на класс типа в объявлении:

>data (Ord k) => Map k v = ...

Тем не менее в языке Haskell принято соглашение никогда не использовать ограничения класса типов при объявлении типов данных. Почему? Потому что серьёзных преимуществ мы не получим, но в конце концов будем использовать всё больше ограничений, даже если они не нужны. Поместим ли мы ограничение >(Ord k) в декларацию типа или не поместим – всё равно придётся указывать его при объявлении функций, предполагающих, что ключ может быть упорядочен. Но если мы не поместим ограничение в объявлении типа, нам не придётся писать его в тех функциях, которым неважно, может ключ быть упорядочен или нет. Пример такой функции – >toList :: Map k a –> [(k, a)]. Если бы >Map k a имел ограничение типа в объявлении, тип для функции >toList был бы таким: >toList :: (Ord k) => Map k a –> [(k, a)], даже несмотря на то что функция не сравнивает элементы друг с другом.

Так что не помещайте ограничения типов в декларации типов данных, даже если это имело бы смысл, потому что вам всё равно придётся помещать ограничения в декларации типов функций.

Векторы судьбы

Давайте реализуем трёхмерный вектор и несколько операций для него. Мы будем использовать параметризованный тип, потому что хоть вектор и содержит только числовые параметры, он должен поддерживать разные типы чисел.

>data Vector a = Vector a a a deriving (Show)


>vplus :: (Num a) => Vector a –> Vector a –> Vector a

>(Vector i j k) `vplus` (Vector l m n) = Vector (i+l) (j+m) (k+n)


>scalarProd :: (Num a) => Vector a –> Vector a –> a

>(Vector i j k) `scalarProd` (Vector l m n) = i*l + j*m + k*n


>vmult :: (Num a) => Vector a –> a –> Vector a

>(Vector i j k) `vmult` m = Vector (i*m) (j*m) (k*m)

Функция >vplus складывает два вектора путём сложения соответствующих координат. Функция >scalarProd используется для вычисления скалярного произведения двух векторов, функция >vmult – для умножения вектора на константу.

Эти функции могут работать с типами >Vector Int, >Vector Integer, >Vector Float и другими, до тех пор пока тип-параметр >a из определения >Vector>a принадлежит классу типов >Num. По типам функций можно заметить, что они работают только с векторами одного типа, и все координаты вектора также должны иметь одинаковый тип. Обратите внимание на то, что мы не поместили ограничение класса