Functor

This post provides vivid explanation for Functors in Haskell. However, I still find it confusing why we have functors in Haskell.

In order to really understand functors, we must explore the origin of this concept. According to wikipedia, “a functor is a type of mapping between categories”, but “mapping” relation isn’t very obvious to me.

The signature of fmap is:

1
fmap :: (a->b) -> f a -> f b

If we rewrite it to be (the parentheses was implicit before):

1
fmap :: (a->b) -> (f a -> f b)

we could interpret that fmap takes one argument, one function with type a->b, and convert it into another function on new types (new domain), f a -> f b. Now the “mapping” referred in the definition becomes explicit.

Intuitively, I understand types in Functor class as containers, that could hold some value.

Monad

All monads in Haskell are type constructors, but not all type constructors are monads. As we will see, monads have to be type constructors for which specific operations are defined and for which specific “monad laws” hold.

return and >>= (bind) are the two passages connecting the pure and impure worlds. Looking at the type of >>=:

(>>=) :: m a -> (a -> m b) -> m b

We can be sure there’s some “unpacking” done in the middle, and how this “unpack” happens is defined by each instance of “Monad” type class.

Many people mistakenly believe that the only reason for having monads in Haskell is to handle non-functional computations i.e. ones that do (file or terminal) I/O, alter global variables, etc. And yet, here I showed you a monadic computation which can be done perfectly well without monads. In this case, monads are not essential; they’re just very convenient. And that’s why I said that even though the original reason for adding monads to Haskell had to do with dealing with inherently non-functional kinds of computations (like computations involving I/O), they turned out to have a far greater applicability. That’s why they’re neat.

f :: Int -> Maybe Int
f x = if x `mod` 2 == 0 then Nothing else Just (2 * x)

g :: Int -> Maybe Int
g x = if x `mod` 3 == 0 then Nothing else Just (3 * x)

h :: Int -> Maybe Int
h x = if x `mod` 5 == 0 then Nothing else Just (5 * x)

k = f >=> g >=> h

k x = case f x of
    Nothing -> Nothing
    Just y  -> case g y of
        Nothing -> Nothing
        Just z  -> h z

Given (>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c), it’s obvious how it could be constructed using bind above.

Since Monad is a subclass of Functor, it could be understood as containers, where sequencing operations are defined.

Applicative vs Monad

Applicative is something between Functor and Monad; it’s a subclass of Functor, and the parent class of Monad. For Applicative types, sequencing operations are defined as well, so it’s a bit sutble to pinpoint the difference between Applicative and Monad.

The following example illustrates that Monad support “short-circus” while Applicative would do the function application anyway.

import Control.Applicative (pure, (<*>))

monad_f :: Int -> Maybe Int
monad_f x = return x

monad_f' :: Int -> Maybe Int
monad_f' x = undefined

applicative_f :: Maybe (Int -> Int)
applicative_f = return id

applicative_f' :: Maybe (Int -> Int)
applicative_f' = undefined

x :: Maybe Int
x = Nothing

main = do
  print $ x >>= monad_f
  print $ x >>= monad_f'
  print $ applicative_f <*> x
  print $ applicative_f' <*> x

The output is:

Nothing
Nothing
Nothing
test.hs: Prelude.undefined

Reference