Вы можете объявить экземпляр Functor
только для параметризованного типа (точнее, * -> *
): тип, который все еще нуждается в дополнительном (и ровно одном) параметре типа.
Итак, сначала нам нужно ввести параметр типа.Даже если вы никогда не планируете использовать что-то еще, кроме Int
s, мы можем легко абстрагироваться от этого:
data Structure <b>a</b> = Structure [<b>a</b>] Bool Int
Мы можем, например, объявить StructureA
как синомим типа :
type StructureA = Structure Int
Теперь мы можем сделать его экземпляром Functor
, написав:
instance Functor <b>Structure</b> where
fmap f (Structure as b c) = ...
Обратите внимание, что мы здесь не пишем (Structure a)
, но Structure
, поскольку, как мы уже говорили, fmap
может свободно изменять тип, над которым работает коллекция: функция f
может иметь, например, тип Int -> Char
для преобразования Structure Int
вStructure Char
.
Теперь нам еще нужно реализовать fmap
.fmap
имеет тип fmap :: Functor f => (a -> b) -> f a -> f b
, что означает, что он принимает функцию, и в данном случае Structure a
, и создает Structure b
.Исходя из вашего вопроса (и принятых нами проектных решений), единственная часть, которую мы можем отобразить, это первый параметр, поэтому мы создаем новый Structure
, где второй параметр является результатом fmap f
, но затемвторой параметр, так:
instance Functor Structure where
fmap f (Structure as b c) = <b>Structure (fmap f as) b c</b>