class Functor_ (t :: * -> *) where
fmap :: (a -> b) -> t a -> t b
Notice that we’re explicitly writing KindSignatures
just for emphasis.
Two laws:
fmap id = id
fmap (f . g) = fmap f . fmap g
Recall mapMaybe
from before:
instance Functor_ Maybe where
-- fmap :: (a -> b) -> Maybe a -> Maybe b
fmap f Nothing = Nothing
fmap f (Just x) = Just $ f x
instance Functor_ [] where
-- fmap :: (a -> b) -> [] a -> [] b
-- fmap :: (a -> b) -> [a] -> [b]
fmap = map
The pair constructor (,)
has kind * -> * -> *
. So, to be a Functor_
, need to partially apply it to one type.
The type signature for fmap
in the class definition refers to bound type variables a
and b
. So we should think a bit before choosing new variables. One option is to choose some other variable, say x
:
instance Functor_ ((,) x) where
-- fmap :: (a -> b) -> (,) x a -> (,) x b
-- fmap :: (a -> b) -> (x, a) -> (x, b)
This is fine, but an alternative is to use a
as the first argument to the pair type and then rename the a
and b
in the fmap
signature to b
and b'
, respectively. I prefer this setup, because it is common to refer to the component types of a pair with variables a
and b
, and the similarity between variables b
and b'
helps to remember their relationship.
instance Functor_ ((,) a) where
-- fmap :: (b -> b') -> (,) a b -> (,) a b'
-- fmap :: (b -> b') -> (a, b) -> (a, b')
fmap f (a, b) = (a, f b)
data Either a b
= Left a
| Right b
A very general mapping function would take a function for each variant:
mapEither :: (a -> a') -> (b -> b') -> Either a b -> Either a' b'
mapEither f g (Left a) = Left $ f a
mapEither f g (Right b) = Right $ g b
But the type of this function doesn’t fit the requirements of fmap
. Like (,)
, need to partially apply Either
to one type. So, must implement “mapRight
”:
instance Functor_ (Either a) where
-- fmap :: (b -> b') -> Either a b -> Either a b'
fmap f (Left a) = Left a
fmap f (Right b) = Right $ f b
This is still useful: transform only Right
values while simply propagating Left
values (often used to represent errors).
Consider the following seemingly equivalent definition — the Left
equation usings a variable left_a
to match the values Left a
:
instance Functor_ (Either a) where
-- fmap :: (b -> b') -> Either a b -> Either a b'
fmap f (Right b) = Right $ f b
fmap f left_a = left_a
This version does does not typecheck, however, because Haskell assigns only one type to every expression. The variable left_a
is assigned type Either a b
, but what is needed on the right-hand side is an Either a b'
. The value bound to left_a
(Left a
for some a
) can be assigned the desired type, but the variable binding that value has been assigned a different type.
instance Functor_ ((->) t) where
-- fmap :: (a -> b) -> ((->) t) a -> ((->) t) b
-- fmap :: (a -> b) -> (t -> a) -> (t -> b)
-- fmap :: (a -> b) -> (t -> a) -> t -> b
-- fmap f g = \t -> f (g t)
-- fmap f g = f . g
fmap = (.)
Here’s a really simple wrapper type. Recall that we often will use newtype
, rather than data
, to define datatypes with exactly one one-argument constructor…
newtype Box a = Box a
… and that we can use record type syntax to automatically derive accessor, or projection, functions.
newtype Box a =
Box { unbox :: a }
Even without using record type syntax, we could have, of course, written our own unbox
function. Although it is certainly nice to have these projection functions automatically derived, the “real” reason we will often define wrapper types (with record type constructors) in what follows is to work around the rule that, for each type class, there can be at most one instance per type
But while we’re here, let’s observe that Box
is a Functor_
.
instance Functor_ Box where
-- fmap :: (a -> b) -> Box a -> Box b
-- fmap f (Box a) = Box (f a)
-- fmap f ia = Box (f (unbox ia))
-- fmap f ia = (Box . f . unbox) ia
fmap f = Box . f . unbox
This Box
type is defined in the standard library with different names:
newtype Identity a = Identity { runIdentity :: a }
type AssocList k v = [(k, v)]
type AssocList k v = [] (k, v)
type AssocList k v = [] ((,) k v)
What if we want to fmap
a function f
over an association list of type AssocList k v
? The function f
must have type (k,v) -> b
for some b
. That’s fine, but if want to map over just the values in each pair and keep the keys the same, a pattern that seems quite desirable, in practice? What we would like is the composition of the behavior of fmap
for lists (i.e. map
) and for pairs (in the Functor_ ((,) a)
instance above).
Haskell’s language of types provides (curried) type application (including partial application), but there aren’t enough type-level computational features to define a “type-level lambda” equivalent to “\v -> [] ((,) k v)
”. So we can’t create an instance definition for AssocList
directly (via the []
and (,)
and types). Neither can we via the name AssocList
, because it is an alias rather than a “real” type. Instances cannot be declaried via aliases to help enforce the one-type, one-instance restriction.
Thus, we must create a wrapper type.
newtype AssocList k v =
BoxAssocList { unboxAssocList :: [(k, v)] }
AssocList
is a Functor_
via a combination of fmap
for lists and fmap
for pairs:
instance Functor_ (AssocList k) where
-- fmap :: (a -> b) -> (AssocList k) a -> (AssfocList k) b
-- fmap :: (a -> b) -> AssocList k a -> AssocList k b
-- fmap f (BoxAssocList kvs) = BoxAssocList $ map (fmap f) kvs
-- fmap f box = BoxAssocList $ map (fmap f) (unboxAssocList box)
-- fmap f box = BoxAssocList $ map (fmap f) . unboxAssocList $ box
fmap f = BoxAssocList . map (fmap f) . unboxAssocList
Thinking back to the “type-level lambda” we wanted to write, it turns out we can factor out that pattern of composition in a more general wrapper type than AssocList
above:
newtype Compose f g x =
Compose { getCompose :: f (g x) }
instance (Functor_ f, Functor_ g) => Functor_ (Compose f g) where
-- fmap :: (a -> b) -> Compose f g x -> Compose f g x
-- fmap f (Compose x) = Compose (fmap (fmap f) x)
fmap f = Compose . fmap (fmap f) . getCompose
And then:
type AssocList k v =
Compose [] ((,) k) v
Or:
type AssocList k =
Compose [] ((,) k)
Functor_
to Functor
The Functor
class is defined in Data.Functor
— and exposed by Prelude
.
class Functor f where
fmap :: (a -> b) -> f a -> f b
As alluded to in the “Monad Roadmap”, there is an infix operator defined to be fmap
.
(<$>) = fmap
The expression f <$> x
resembles, and is equivalent to, the function application fmap f $ x
.