Haskell Language

Functor

Introduction#

Functor is the class of types f :: * -> * which can be covariantly mapped over. Mapping a function over a data structure applies the function to all the elements of the structure without changing the structure itself.

Remarks#

A Functor can be thought of as a container for some value, or a computation context. Examples are Maybe a or [a]. The Typeclassopedia article has a good write-up of the concepts behind Functors.

To be considered a real Functor, an instance has to respect the 2 following laws:

Identity

fmap id == id

Composition

fmap (f . g) = (fmap f) . (fmap g)

Common instances of Functor

Maybe

Maybe is a Functor containing a possibly-absent value:

instance Functor Maybe where
    fmap f Nothing = Nothing
    fmap f (Just x) = Just (f x)

Maybe’s instance of Functor applies a function to a value wrapped in a Just. If the computation has previously failed (so the Maybe value is a Nothing), then there’s no value to apply the function to, so fmap is a no-op.

> fmap (+ 3) (Just 3)
Just 6
> fmap length (Just "mousetrap")
Just 9
> fmap sqrt Nothing
Nothing

We can check the functor laws for this instance using equational reasoning. For the identity law,

fmap id Nothing
Nothing  -- definition of fmap
id Nothing  -- definition of id

fmap id (Just x)
Just (id x)  -- definition of fmap
Just x  -- definition of id
id (Just x)  -- definition of id

For the composition law,

(fmap f . fmap g) Nothing
fmap f (fmap g Nothing)  -- definition of (.)
fmap f Nothing  -- definition of fmap
Nothing  -- definition of fmap
fmap (f . g) Nothing  -- because Nothing = fmap f Nothing, for all f

(fmap f . fmap g) (Just x)
fmap f (fmap g (Just x))  -- definition of (.)
fmap f (Just (g x))  -- definition of fmap
Just (f (g x))  -- definition of fmap
Just ((f . g) x)  -- definition of (.)
fmap (f . g) (Just x)  -- definition of fmap

Lists

Lists’ instance of Functor applies the function to every value in the list in place.

instance Functor [] where
    fmap f [] = []
    fmap f (x:xs) = f x : fmap f xs

This could alternatively be written as a list comprehension: fmap f xs = [f x | x <- xs].

This example shows that fmap generalises map. map only operates on lists, whereas fmap works on an arbitrary Functor.

The identity law can be shown to hold by induction:

-- base case
fmap id []
[]  -- definition of fmap
id []  -- definition of id

-- inductive step
fmap id (x:xs)
id x : fmap id xs  -- definition of fmap
x : fmap id xs  -- definition of id
x : id xs  -- by the inductive hypothesis
x : xs  -- definition of id
id (x : xs)  -- definition of id

and similarly, the composition law:

-- base case
(fmap f . fmap g) []
fmap f (fmap g [])  -- definition of (.)
fmap f []  -- definition of fmap
[]  -- definition of fmap
fmap (f . g) []  -- because [] = fmap f [], for all f

-- inductive step
(fmap f . fmap g) (x:xs)
fmap f (fmap g (x:xs))  -- definition of (.)
fmap f (g x : fmap g xs)  -- definition of fmap
f (g x) : fmap f (fmap g xs)  -- definition of fmap
(f . g) x : fmap f (fmap g xs)  -- definition of (.)
(f . g) x : fmap (f . g) xs  -- by the inductive hypothesis
fmap (f . g) xs  -- definition of fmap

Functions

Not every Functor looks like a container. Functions’ instance of Functor applies a function to the return value of another function.

instance Functor ((->) r) where
    fmap f g = \x -> f (g x)

Note that this definition is equivalent to fmap = (.). So fmap generalises function composition.

Once more checking the identity law:

fmap id g
\x -> id (g x)  -- definition of fmap
\x -> g x  -- definition of id
g  -- eta-reduction
id g  -- definition of id

and the composition law:

(fmap f . fmap g) h
fmap f (fmap g h)  -- definition of (.)
fmap f (\x -> g (h x))  -- definition of fmap
\y -> f ((\x -> g (h x)) y)  -- definition of fmap
\y -> f (g (h y))  -- beta-reduction
\y -> (f . g) (h y)  -- definition of (.)
fmap (f . g) h  -- definition of fmap

Class Definition of Functor and Laws

class Functor f where
    fmap :: (a -> b) -> f a -> f b

One way of looking at it is that fmap lifts a function of values into a function of values in a context f.

A correct instance of Functor should satisfy the functor laws, though these are not enforced by the compiler:

fmap id = id                    -- identity
fmap f . fmap g = fmap (f . g)  -- composition

There’s a commonly-used infix alias for fmap called <$>.

infixl 4 <$>
(<$>) :: Functor f => (a -> b) -> f a -> f b
(<$>) = fmap

Replacing all elements of a Functor with a single value

The Data.Functor module contains two combinators, <$ and $>, which ignore all of the values contained in a functor, replacing them all with a single constant value.

infixl 4 <$, $>

<$ :: Functor f => a -> f b -> f a
(<$) = fmap . const

$> :: Functor f => f a -> b -> f b
($>) = flip (<$)

void ignores the return value of a computation.

void :: Functor f => f a -> f ()
void = (() <$)

Polynomial functors

There’s a useful set of type combinators for building big Functors out of smaller ones. These are instructive as example instances of Functor, and they’re also useful as a technique for generic programming, because they can be used to represent a large class of common functors.

The identity functor

The identity functor simply wraps up its argument. It’s a type-level implementation of the I combinator from SKI calculus.

newtype I a = I a

instance Functor I where
    fmap f (I x) = I (f x)

I can be found, under the name of Identity, in the Data.Functor.Identity module.

The constant functor

The constant functor ignores its second argument, containing only a constant value. It’s a type-level analogue of const, the K combinator from SKI calculus.

newtype K c a = K c

Note that K c a doesn’t contain any a-values; K () is isomorphic to Proxy. This means that K’s implementation of fmap doesn’t do any mapping at all!

instance Functor (K c) where
    fmap _ (K c) = K c

K is otherwise known as Const, from Data.Functor.Const.

The remaining functors in this example combine smaller functors into bigger ones.

Functor products

The functor product takes a pair of functors and packs them up. It’s analogous to a tuple, except that while (,) :: * -> * -> * operates on types *, (:*:) :: (* -> *) -> (* -> *) -> (* -> *) operates on functors * -> *.

infixl 7 :*:
data (f :*: g) a = f a :*: g a

instance (Functor f, Functor g) => Functor (f :*: g) where
    fmap f (fx :*: gy) = fmap f fx :*: fmap f gy

This type can be found, under the name Product, in the Data.Functor.Product module.

Functor coproducts

Just like :*: is analogous to (,), :+: is the functor-level analogue of Either.

infixl 6 :+:
data (f :+: g) a = InL (f a) | InR (g a)

instance (Functor f, Functor g) => Functor (f :+: g) where
    fmap f (InL fx) = InL (fmap f fx)
    fmap f (InR gy) = InR (fmap f gy)

:+: can be found under the name Sum, in the Data.Functor.Sum module.

Functor composition

Finally, :.: works like a type-level (.), taking the output of one functor and plumbing it into the input of another.

infixr 9 :.:
newtype (f :.: g) a = Cmp (f (g a))

instance (Functor f, Functor g) => Functor (f :.: g) where
    fmap f (Cmp fgx) = Cmp (fmap (fmap f) fgx)

The Compose type can be found in Data.Functor.Compose

Polynomial functors for generic programming

I, K, :*:, :+: and :.: can be thought of as a kit of building blocks for a certain class of simple datatypes. The kit becomes especially powerful when you combine it with fixed points because datatypes built with these combinators are automatically instances of Functor. You use the kit to build a template type, marking recursive points using I, and then plug it into Fix to get a type that can be used with the standard zoo of recursion schemes.

Name As a datatype Using the functor kit
Pairs of values data Pair a = Pair a a type Pair = I :*: I
Two-by-two grids type Grid a = Pair (Pair a) type Grid = Pair :.: Pair
Natural numbers data Nat = Zero | Succ Nat type Nat = Fix (K () :+: I)
Lists data List a = Nil | Cons a (List a) type List a = Fix (K () :+: K a :*: I)
Binary trees data Tree a = Leaf | Node (Tree a) a (Tree a) type Tree a = Fix (K () :+: I :*: K a :*: I)
Rose trees data Rose a = Rose a (List (Rose a)) type Rose a = Fix (K a :*: List :.: I)

This “kit” approach to designing datatypes is the idea behind generic programming libraries such as generics-sop. The idea is to write generic operations using a kit like the one presented above, and then use a type class to convert arbitrary datatypes to and from their generic representation:

class Generic a where
    type Rep a  -- a generic representation built using a kit
    to :: a -> Rep a
    from :: Rep a -> a

Functors in Category Theory

A Functor is defined in category theory as a structure-preserving map (a ‘homomorphism’) between categories. Specifically, (all) objects are mapped to objects, and (all) arrows are mapped to arrows, such that the category laws are preserved.

The category in which objects are Haskell types and morphisms are Haskell functions is called Hask. So a functor from Hask to Hask would consist of a mapping of types to types and a mapping from functions to functions.

The relationship that this category theoretic concept bears to the Haskell programming construct Functor is rather direct. The mapping from types to types takes the form of a type f :: * -> *, and the mapping from functions to functions takes the form of a function fmap :: (a -> b) -> (f a -> f b). Putting those together in a class,

class Functor (f :: * -> *) where
    fmap :: (a -> b) -> f a -> f b

fmap is an operation that takes a function (a type of morphism), :: a -> b, and maps it to another function, :: f a -> f b. It is assumed (but left to the programmer to ensure) that instances of Functor are indeed mathematical functors, preserving Hask’s categorical structure:

fmap (id {- :: a -> a -})  ==  id {- :: f a -> f a -}
fmap (h . g)               ==  fmap h . fmap g

fmap lifts a function :: a -> b into a subcategory of Hask in a way that preserves both the existence of any identity arrows, and the associativity of composition.


The Functor class only encodes endofunctors on Hask. But in mathematics, functors can map between arbitrary categories. A more faithful encoding of this concept would look like this:

class Category c where
    id  :: c i i
    (.) :: c j k -> c i j -> c i k

class (Category c1, Category c2) => CFunctor c1 c2 f where
    cfmap :: c1 a b -> c2 (f a) (f b)

The standard Functor class is a special case of this class in which the source and target categories are both Hask. For example,

instance Category (->) where        -- Hask
    id    = \x -> x
    f . g = \x -> f (g x)

instance CFunctor (->) (->) [] where
    cfmap = fmap

Deriving Functor

The DeriveFunctor language extension allows GHC to generate instances of Functor automatically.

{-# LANGUAGE DeriveFunctor #-}

data List a = Nil | Cons a (List a) deriving Functor

-- instance Functor List where            -- automatically defined
--   fmap f Nil = Nil
--   fmap f (Cons x xs) = Cons (f x) (fmap f xs)

map :: (a -> b) -> List a -> List b
map = fmap

This modified text is an extract of the original Stack Overflow Documentation created by the contributors and released under CC BY-SA 3.0 This website is not affiliated with Stack Overflow