. Если мы посмотрим на определение класса
>Functor
ещё раз:
>class Functor f where
> fmap :: (a –> b) –> f a –> f b
то увидим, что переменная типа >f
используется как тип, принимающий один конкретный тип для того, чтобы создать другой. Мы знаем, что возвращается конкретный тип, поскольку он используется как тип значения в функции. Из этого можно заключить, что типы, которые могут «подружиться» с классом >Functor
, должны иметь сорт >*
>–>
>*
.
Ну а теперь займёмся тип-фу. Посмотрим на определение такого класса типов:
>class Tofu t where
> tofu :: j a –> t a j
Объявление выглядит странно. Как мы могли бы создать тип, который будет иметь экземпляр такого класса? Посмотрим, каким должен быть сорт типа. Так как тип >j a
используется как тип значения, который функция >tofu
принимает как параметр, у типа >j a
должен быть сорт *. Мы предполагаем сорт >*
для типа >a
и, таким образом, можем вывести, что тип >j
должен быть сорта >* –> *
. Мы видим, что тип >t
также должен производить конкретный тип, и что он принимает два типа. Принимая во внимание, что у типа >a
сорт >*
и у типа >j
сорт >* –> *
, мы выводим, что тип >t
должен быть сорта >* –> (* –> *) –> *
. Итак, он принимает конкретный тип >(a)
и конструктор типа, который принимает один конкретный тип >(j),
и производит конкретный тип. Вау!
Хорошо, давайте создадим тип такого сорта: >* –> (* –> *) –> *
. Вот один из вариантов:
>data Frank a b = Frank {frankField :: b a} deriving (Show)
Откуда мы знаем, что этот тип имеет сорт >* –> (* –> *) – > *
? Именованные поля в алгебраических типах данных сделаны для того, чтобы хранить значения, так что они по определению должны иметь сорт >*
. Мы предполагаем сорт >*
для типа >a
; это означает, что тип >b
принимает один тип как параметр. Таким образом, его сорт – >* –>
>*
. Теперь мы знаем сорта типов >a
и >b
; так как они являются параметрами для типа >Frank
, можно показать, что тип >Frank
имеет сорт >* –> (* –> *) – > *
. Первая >*
обозначает сорт типа >a
; >(*
>–> *)
обозначает сорт типа >b
. Давайте создадим несколько значений типа >Frank
и проверим их типы.
>ghci> :t Frank {frankField = Just "ХА-ХА"}
>Frank {frankField = Just "ХА-ХА"} :: Frank [Char] Maybe
>ghci> :t Frank {frankField = Node 'a' EmptyTree EmptyTree}
>Frank {frankField = Node 'a' EmptyTree EmptyTree} :: Frank Char Tree
>ghci> :t Frank {frankField = "ДА"}
>Frank {frankField = "ДА"} :: Frank Char []
Гм-м-м… Так как поле >frankField
имеет тип вида >a b
, его значения должны иметь типы похожего вида. Например, это может быть >Just "ХА-ХА"
, тип в этом примере – >Maybe [Char]
, или >['Д','А']
(тип