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