Экземпляр класса 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

Nothing

ghci> fmap (*2) (Just 200)

Just 400

ghci> fmap (*2) Nothing

Nothing