Functors aren’t as hard as they sound

Suppose we have a functor. Let’s call it “My Functor”, because that’s a nice friendly name. I’ll call it MF for short.

Now, a functor MF is—according to higher sources—something which takes any type a to a type MF(a). Let’s see how we can accomplish that with our functor, using Haskell code:

data MyFunctor a = Construct a

This is quite simply a type constructor for our functor for any type a. For example, we can use it on the Integer type as Construct Integer, which will give us the type MyFunctor Integer.

That’s half the definition of a functor. The other half is that it also converts any function f \colon a \to b to a function MF(f) \colon MF(a) \to MF(b). This means for any function, it converts that function to work on stuff “inside” the functor.

In terms of our Haskell code, this means that we have a function that acts on another function to modify it. This function is usually called “fmap” (for “functor map” or something similar). This function has the type:

fmap :: (a -> b) -> (MyFunctor a -> MyFunctor b)

It takes any function f\colon a\to b to MF(f) \colon MF(a) \to MF(b). Now we can define the function itself. This is the part which will be the most unique for our functor, and we can define it how we like. For our functor, this isn’t going to be very interesting:

fmap f (MyFunctor a) = MyFunctor (f a)

We unwrap the object a from our MyFunctor, apply the function f, then wrap it back up.

Now there are two laws that fmap must satisfy in order for this to be a “real” functor. In Haskell these are:

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

These can’t be enforced from “within” Haskell, but you must keep them in mind when writing your functor. If you’d like, you can check that these laws hold for our MyFunctor defined above.

Now we have our entire functor:

data MyFunctor a = Construct a
fmap f (MyFunctor a) = MyFunctor (f a)

…and in fact, Haskell has a built-in class for functors:

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

We can declare our MyFunctor to be a Functor like this:

instance Functor MyFunctor where
  fmap f (MyFunctor a) = MyFunctor (f a)

…which still doesn’t seem very interesting.

An interesting functor is List. This is defined as:

data List a = Empty | Cons a (List a)
 
instance Functor List where
  fmap f (Cons first rest) = Cons (f first) (fmap f rest)
  fmap f Empty = Empty

…wherein the functor takes a type a to a list of a, and fmap is the familiar map list function, only expressed in a functor context.

Post a Comment

Your email is never published nor shared. Required fields are marked *