पाठ 10 / 25
Functor: Mapping Inside a Context
Understand fmap and the Functor laws with lists, Maybe, Either and your own types.
fmap generalises map
Many types are containers or contexts around values: a list holds many values, Maybe holds zero or one, Either e holds a value or an error, IO will produce a value when run. The Functor class captures the ability to apply a function to the value(s) inside without changing the structure: fmap :: Functor f => (a -> b) -> f a -> f b, with the infix synonym <$>. fmap (+1) (Just 2) is Just 3, fmap (+1) Nothing is Nothing, fmap length (Right "abc") is Right 3, and fmap over IO transforms the result of an action. Instances must obey two laws: mapping id changes nothing (fmap id = id), and mapping a composition equals composing maps (fmap (f . g) = fmap f . fmap g). These laws guarantee fmap only transforms values and never adds, removes or reorders them. You can write instances for your own types, and GHC can derive them with deriving Functor (the DeriveFunctor extension, included in GHC2021). Related helpers: <$ replaces values with a constant, and void discards a result.
fmap over different contexts
The same function is applied inside a list, a Maybe and an Either, keeping their shape.
Functor instances in action
Built-in functors and a derived instance for a tree.
{-# LANGUAGE DeriveFunctor #-}
data Tree a = Leaf | Node (Tree a) a (Tree a)
deriving (Show, Functor)
-- a hand-written instance would be:
-- instance Functor Tree where
-- fmap _ Leaf = Leaf
-- fmap f (Node l x r) = Node (fmap f l) (f x) (fmap f r)
fromList :: Ord a => [a] -> Tree a
fromList = foldr insert Leaf
where
insert x Leaf = Node Leaf x Leaf
insert x t@(Node l y r)
| x < y = Node (insert x l) y r
| x > y = Node l y (insert x r)
| otherwise = t
main :: IO ()
main = do
print (fmap (* 2) [1, 2, 3 :: Int]) -- [2,4,6]
print ((+ 1) <$> Just (41 :: Int)) -- Just 42
print (length <$> (Right "abc" :: Either String String)) -- Right 3
print (fmap show (fromList [3, 1, 2 :: Int])) -- a Tree of Strings
n <- length <$> getLine -- fmap over an IO action
print nGift wrapping
fmap is a helper who can change the gift inside any kind of wrapping (a box, an envelope, a bag that might be empty) without tearing the wrapping or changing how many gifts there are.
त्वरित जाँच: What is `fmap (+1) Nothing`?
- Just 1
- An error
- Nothing
- 1
Answer
Nothing — Mapping over Nothing leaves Nothing: there is no value to transform.