Lecture 5 — 2018-01-30
Functor
This lecture is written in literate Haskell; you can download the raw source.
We talked about the Functor
type class, defined as:
class Functor f where
fmap :: (a -> b) -> f a -> f b
-- LAW: fmap id = id
When you see Functor
, think “Mappable”: a type with a Functor
instance is one that can be mapped over, i.e., you can change all of the values in the holes while preserving the structure.
Our first example was lists, which was easy: we already know the map
function well!
instance Functor ([]) where
fmap = map
We defined a separate type, Box
, to see how far we could go:
data Box a = B a deriving Show
instance Functor Box where
fmap f (B v) = B (f v)
We saw how to do it for Maybe
, too:
instance Functor Maybe where
fmap f Nothing = Nothing
fmap f (Just v) = Just (f v)
We carefully preserve the structure of our argument: if we’re given a Nothing
, the types themselves force us to return a Nothing
. If we’re given Just v
, we could return Nothing
, but then we’d violate the Functor
law that mapping with the identity is the identity.
We also defined ‘readers’, that is, functions whose domain is some fixed type r
. Readers disrupt our notion of interpreting functorial type constructors of kind * -> *
as mere ‘containers’, that is, things that might hold some value. Readers are functions—there’s no way to rip open and get the value out of a function. We’ll have to broaden our intuition—that functors are ‘computational contexts’ that will produce a value—perhaps by containing one, perhaps by other means.
In any case, to figure out the instance for (->) r
, we let types be our guide.
instance Functor ((->) r) where
-- fmap :: (a -> b) -> ((->) r a) -> ((->) r b)
-- i.e. (a -> b) -> (r -> a) -> (r -> b)
fmap fab fra = \r -> fab (fra r)
We didn’t define an instance for an Either
type, but here’s the one Haskell gives you: the first parameter is fixed, just like readers. Note that Either :: * -> * -> *
but for a type e
, we have Either e :: * -> *
—which is exactly the kind of thing that can be a Functor
.
instance Functor (Either e) where
fmap f (Left err) = Left err
fmap f (Right v) = Right (f v)
You’ll notice I used the suggestive name err
for the left-hand side. Haskell uses the Either
type to talk about computations that could fail: if an error occurs, we return a Left
; if we succeed, we return a Right
. Don’t worry: this choice has more to do the synonymy of “right” and “correct” than politics.