Functors, Applicatives, Monads¶

Functors¶

In [1]:
inc :: [Int] -> [Int]
inc []     = []
inc (n:ns) = n+1 : inc ns

sqr :: [Int] -> [Int]
sqr []     = []
sqr (n:ns) = n^2 : sqr ns
Use map
Found:
inc [] = [] inc (n : ns) = n + 1 : inc ns
Why Not:
inc ns = map (+ 1) ns
Use map
Found:
sqr [] = [] sqr (n : ns) = n ^ 2 : sqr ns
Why Not:
sqr ns = map (^ 2) ns

Functors abstract the idea of mapping a function over each element of a data structure. Rather than writing dedicated mapping functions for every parameterised type (such as map for lists), Haskell captures this pattern with the Functor type class.

In [2]:
map :: (a -> b) -> [a] -> [b]
map g []     = []
map g (x:xs) = g x : map g xs
Use map
Found:
map g [] = [] map g (x : xs) = g x : map g xs
Why Not:
map g xs = map g xs

The class declaration:

In [3]:
class Functor f where
  fmap :: (a -> b) -> f a -> f b
  • Type Parameter f: Must be a parameterised type (a type constructor taking one argument, such as [], Maybe, or IO).

  • fmap: Applies a pure function (a -> b) to the element(s) inside f a to return a new structure f b.

Standard and Custom Functor Examples¶

Lists¶

In [4]:
instance Functor [] where
  -- fmap :: (a -> b) -> [a] -> [b]
  fmap = map
In [5]:
fmap (+1) [1,2,3]
[2,3,4]
In [6]:
map (`replicate` "*") [1,2,3]
[["*"],["*","*"],["*","*","*"]]

Maybe¶

In [7]:
instance Functor Maybe where
  -- fmap :: (a -> b) -> Maybe a -> Maybe b
  fmap g Nothing  = Nothing
  fmap g (Just x) = Just (g x)
In [8]:
fmap (+1) Nothing
Nothing
In [9]:
fmap (*2) (Just 3)
Just 6
In [10]:
fmap not (Just False)
Just True

Binary Tree (Leaf Data)¶

In [11]:
data Tree a = Leaf a | Node (Tree a) (Tree a) deriving Show
In [12]:
instance Functor Tree where
  -- fmap :: (a -> b) -> Tree a -> Tree b
  fmap g (Leaf x)   = Leaf (g x)
  fmap g (Node l r) = Node (fmap g l) (fmap g r)
In [13]:
fmap length (Leaf "abc")
Leaf 3
In [14]:
fmap even (Node (Leaf 1) (Leaf 2))
Node (Leaf False) (Leaf True)

Input Output (IO)¶

The above 3 examples can be viewed as "container" functors! The next one is different. IO functor represents input/output actions!

In [15]:
instance Functor IO where
  -- fmap :: (a -> b) -> IO a -> IO b
  fmap g mx = do { x <- mx; return (g x) }
Use <$>
Found:
do x <- mx return (g x)
Why Not:
do g <$> mx
In [16]:
fmap show (return True)
Redundant fmap
Found:
fmap show (return True)
Why Not:
return (show True)
"True"

Generic functions over Functors¶

In [17]:
inc :: Functor f => f Int -> f Int
inc = fmap (+1)
In [18]:
inc (Just 1)
Just 2
In [19]:
inc [1, 2, 3, 4, 5]
[2,3,4,5,6]
In [20]:
inc (Node (Leaf 1) (Leaf 2))
Node (Leaf 2) (Leaf 3)

Equational Functor Laws¶

Every valid instance of Functor must obey two fundamental laws:

  1. Identity Preservation: $$\text{fmap id} = \text{id}$$

  2. Composition Preservation: $$\text{fmap } (g \cdot h) = \text{fmap } g \cdot \text{fmap } h$$

What if a functor does not satisfy the functor laws?¶

In [1]:
instance Functor [] where
    -- fmap :: (a -> b) -> f a -> f b
    fmap _ [] = []
    fmap g (x:xs) = fmap g xs ++ [g x] -- we are reversing the items in the list!
<interactive>:1:10: error: [GHC-59692]
    Duplicate instance declarations:
      instance Functor [] -- Defined at <interactive>:1:10
      instance Functor [] -- Defined in ‘GHC.Base’

fmap id [1,2] = [2,1] id [1,2] = [1,2]

fmap (not . even) [1,2] = [False, True] (fmap not . fmap even) [1,2] = fmap not (fmap even [1,2]) = fmap not [False,True] = [True, False]