Functors in Haskell. Adapted from material by Miran Lipovaca. Functors. Functors are a typeclass, just like Ord, Eq, Show, and all the others. This one is designed to hold things that can be mapped over; for example, lists are part of this typeclass. class Functor f where
Adapted from material by Miran Lipovaca
Functors are a typeclass, just like Ord, Eq, Show, and all the others. This one is designed to hold things that can be mapped over; for example, lists are part of this typeclass.
Only one typeclass method, called fmap. Basically, says: give me a function that takes a and returns b and a “box” with type a inside of it, and I’ll return a “box” with type b inside of it.
Compare fmap to map:
map :: (a -> b) -> [a] -> [b]
So map is a lot like a functor! Here, map takes a function and a list of type a, and returns a list of type b.
In fact, can define map in terms of fmap:
Notice what we wrote:
We did NOT write “instance Functor [a] where…”, since f has to be a type constructor that takes one type.
Here, [a] is already a concrete type, while  is a type constructor that takes one type and can produce many types, like [Int], [String], [[Int]], etc.
Again, we did NOT write “instance Functor (Maybe m) where…”, since functor wants a type constructor.
Mentally replace the f’s with Maybe, so fmap acts like (a -> b) -> Maybe a -> Maybe b.
If we put (Maybe m), would have (a -> b) -> (Maybe m) a -> (Maybe m) b, which looks wrong.
This is slightly different than our last definition. Here, trees will be of the form:
Node 5 (Node 3 (Node 1 EmptyTree EmptyTree) (Node 4 EmptyTree EmptyTree)) (Node 6 EmptyTree EmptyTree)
Some tree functions:
And now to find an element in the tree:
Note: we deliberately assumed we could compare the elements of the nodes so that we could make this a binary search tree.
If this is an “unordered” tree, would need to search both left and right subtrees.
An example run:
Back to functors:
If we looked at fmap as though it were only for trees, it would look something like:
(a -> b) -> Tree a -> Tree b
We can certainly phrase this as a functor, also:
Using the tree functor:
If a type takes 2 parameters, like Either:
Then the signature :k looks like this:
For this situation, we need to partially apply the type constructor until it takes only 1 input.
Remember, Haskell is good at partial evaluations!
instance Functor (Either a) where
fmap :: (b -> c) -> Either a b -> Either a c
Another functor: the <- in the IO class
Example: program 1
There are two rules that every functor should obey. Note that these aren’t enforced by Haskell, but they are important!
First: If we map the identity function over a functor, then the functor we get back should be the same as the original functor. So: fmap id = id.
Second rule: composing two functions and then mapping the resulting function over a functor should be the same as first mapping one function over the functor and then mapping the other. So:
fmap (f . g) = fmap f . fmap g.
Huh? Well, fmap (f . g) (Just x) is (if you go back and look) defined as Just ((f . g) x), which is the same as Just (f (g x)), since we’re just composing functions.
And fmap f (fmap g (Just x)) is fmap f (Just (g x)) which is then Just (f (g x)), so we’re ok!