с такого:
>data Car = Car { company :: String
> , model :: String
> , year :: Int
> } deriving (Show)
на такой:
>data Car a b c = Car { company :: a
> , model :: b
> , year :: c
> } deriving (Show)
Но выиграем ли мы в чём-нибудь? Ответ – вероятно, нет, потому что впоследствии мы всё равно определим функции, которые работают с типом >Car String String Int
. Например, используя первое определение >Car
, мы могли бы создать функцию, которая отображает свойства автомобиля в виде понятного текста:
>tellCar :: Car –> String
>tellCar (Car {company = c, model = m, year = y}) =
> "Автомобиль " ++ c ++ " " ++ m ++ ", год: " ++ show y
>ghci> let stang = Car {company="Форд", model="Мустанг", year=1967}
>ghci> tellCar stang
>"Автомобиль Форд Мустанг, год: 1967"
Приятная маленькая функция. Декларация типа функции красива и понятна. А что если >Car
– это >Car a b c
?
>tellCar :: (Show a) => Car String String a –> String
>tellCar (Car {company = c, model = m, year = y}) =
> "Автомобиль " ++ c ++ " " ++ m ++ ", год: " ++ show y
Мы вынуждены заставить функцию принимать параметр >Car
типа >(Show a) => Car String String a
. Как видите, декларация типа функции более сложна; единственное преимущество, которое здесь имеется, – мы можем использовать любой тип, имеющий экземпляр класса >Show
, как тип для типовой переменной >c
.
>ghci> tellCar (Car "Форд" "Мустанг" 1967)
>"Автомобиль Форд Мустанг, год: 1967"
>ghci> tellCar (Car "Форд" "Мустанг" "тысяча девятьсот шестьдесят седьмой")
>"Автомобиль Форд Мустанг, год: \"тысяча девятьсот шестьдесят седьмой\""
>ghci> :t Car "Форд" "Мустанг" 1967
>Car "Форд" "Мустанг" 1967 :: (Num t) => Car [Char] [Char] t
>ghci> :t Car "Форд" "Мустанг" "тысяча девятьсот шестьдесят седьмой"
>Car "Форд" "Мустанг" "тысяча девятьсот шестьдесят седьмой"
> :: Car [Char] [Char] [Char]
На практике мы всё равно в большинстве случаев использовали бы >Car String String Int
, так что в параметризации типа >Car
большого смысла нет. Обычно мы параметризируем типы, когда для работы нашего типа неважно, что в нём хранится. Список элементов – это просто список элементов, и неважно, какого они типа: список работает вне зависимости от этого. Если мы хотим суммировать список чисел, то в суммирующей функции можем уточнить, что нам нужен именно список чисел. То же самое верно и для типа >Maybe
. Он предоставляет возможность не иметь никакого значения или иметь какое-то одно значение. Тип хранимого значения не важен.
Ещё один известный нам пример параметризованного типа – отображения