Haskell : Typeclasses and Functors

How does Haskell deal with abstracting the functor? It uses the typeclass mechanism.

A typeclass defines a family of types that support a common interface.

class Eq a where
  (==) :: a -> b -> Bool
  • If you want to tell Haskell that a particular type is Eq, you have to declare it an instance of this class and provide the implementation of (==).
data Point = Pt Float Float
instance Eq Pt where
  (==) :: (Pt x1 y1) -> (Pt x2 y2) = x1 == x2 && y1 == y2
  • Notice that, unlike in C++ or Java, you don’t have to specify the Eq class (or interface) when defining Point — you can do it later in client code. Typeclasses are also Haskell’s only mechanism for overloading functions (and operators). We will need that for overloading fmap for different functors.
  • There is one complication, though: a functor is not defined as a type but as a mapping of types (a type constructor). We need a typeclass that’s not a family of types, as was the case with Eq, but a family of type constructors. Fortunately a Haskell typeclass works with type constructors as well as with types. So here’s the definition of the Functor class:
    class Functor f where
        fmap :: (a -> b) -> f a -> f b
  • instance Functor Maybe where
      fmap _ Nothing  = Nothing
      fmap f (Just a) = Just f(a)
  • data List a = Nil | Con a (List a)
    
    instance Functor List where
      fmap _ Nil = Nil
      fmap f (Con a list) = Con (f a) (fmap f list)

     

    Functor Laws (Composition and Identity)

    Reader Functor
    Combination of the type constructor (->) r with the below implementation of fmap is called the reader functor.

  • data (->) r a
    
    instance Functor ((->) r) where
      -- fmap :: (a -> b) -> (r -> a) -> (r -> b)
      fmap f g = f . g -- or (.) f g or fmap = (.)

    Functors as Containers
    Haskell’s laziness, a traditional container, like a list, may actually be implemented as a function. Consider, for instance, an infinite list of natural numbers, which can be compactly defined as:

    nats :: [Integer]
    nats = [1..]

    Functor Composition
    A composition of two functors, when acting on objects, is just the composition of their respective object mappings; and similarly when acting on morphisms.

    Bi-Functors
    Since functors are morphisms in Cat (the category of categories), a lot of intuitions about morphisms — and functions in particular — apply to functors as well.

Writer Functor

type Writer a = (a, String)
(>=>) :: (a -> Writer b) -> (b -> Writer c) -> (a -> Writer c)
m1 >=> m2 = \x -> 
    let (y, s1) = m1 x
        (z, s2) = m2 y
    in (z, s1 ++ s2)
return :: a -> Writer a
return x = (x, "")
fmap f = id >=> (\x -> return (f x))

Leave a comment