, потому что из определения функции
>fmap :: (a –> b) –> f a –> f b
мы видим, что параметр >f
должен быть конструктором типов, принимающим один тип. Выражение >[a]
– это уже конкретный тип (список элементов типа >a
), а вот >[]
– это конструктор типов, который принимает один тип; он может производить такие конкретные типы, как >[Int]
, >[String]
или даже >[[String]]
.
Так как для списков функция >fmap
– это просто >map
, то мы получим одинаковые результаты при их использовании на списках:
>map :: (a –> b) –> [a] –> [b]
>ghci>fmap (*2) [1..3]
>[2,4,6]
>ghci> map (*2) [1..3]
>[2,4,6]
Что случится, если применить функцию >map
или >fmap
к пустому списку? Мы получим опять же пустой список. Но функция >fmap
преобразует пустой список типа >[a]
в пустой список типа >[b]
.
Экземпляр класса Functor для типа Maybe
Типы, которые могут вести себя как контейнеры по отношению к другим типам, могут быть функторами. Можно представить, что списки – это коробки с бесконечным числом отсеков; все они могут быть пустыми, или же один отсек заполнен, а остальные пустые, или несколько из них заполнены. А что ещё умеет быть контейнером для других типов? Например, тип >Maybe
. Он может быть «пустой коробкой», и в этом случае имеет значение >Nothing
, или же в нём хранится какое-то одно значение, например >"ХА-ХА"
, и тогда он равен >Just
>"ХА-ХА"
.
Вот как тип >Maybe
сделан функтором:
>instance Functor Maybe where
> fmap f (Just x) = Just (f x)
> fmap f Nothing = Nothing
Ещё раз обратите внимание на то, как мы записали декларацию >instance Functor Maybe where
вместо >instance Functor (Maybe m) where
– подобно тому как мы делали для класса >YesNo
. Функтор принимает конструктор типа с одним параметром, не конкретный тип. Если вы мысленно замените параметр >f
на >Maybe
, функция >fmap
работает как >(a –> b) –> Maybe a –> Maybe b
, только для типа >Maybe
, что вполне себя оправдывает. Но если заменить >f
на >(Maybe m)
, то получится >(a –> b) –> Maybe m a –> Maybe m b
, что не имеет никакого смысла, так как тип >Maybe
принимает только один тип-параметр.
Как бы то ни было, реализация функции >fmap
довольно проста. Если значение типа >Maybe
– это >Nothing
, возвращается >Nothing
. Если мы отображаем «пустую коробку», мы получим «пустую коробку», что логично. Точно так же функция >map
для пустого списка возвращает пустой список. Если это не пустое значение, а некоторое значение, упакованное в конструктор >Just
, то мы применяем функцию к содержимому >Just
:
>ghci> fmap (++ " ПРИВЕТ, Я ВНУТРИ JUST") (Just "Серьёзная штука.")
>Just "Серьёзная штука. ПРИВЕТ, Я ВНУТРИ JUST"
>ghci> fmap (++ " ПРИВЕТ, Я ВНУТРИ JUST") Nothing