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 :: (a->b) -> f a -> f b
If we rewrite it to be (the parentheses was implicit before):
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 b. Now the “mapping” referred in the definition becomes explicit.
Intuitively, I understand types in
Functor class as containers, that could hold some value.
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.
>>= (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
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> (a -> m c), it’s obvious how it could be constructed using
Monad is a subclass of
Functor, it could be understood as containers, where sequencing operations are defined.
Applicative is something between
Monad; it’s a subclass of
Functor, and the parent class of
sequencing operations are defined as well, so it’s a bit sutble to pinpoint the difference between
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