25

I'm new to functional programming (coming from javascript), and I'm having a hard time telling the difference between the two, which is also messing with my understand of functors vs. monads.

Functor:

class Functor f where
    fmap :: (a -> b) -> f a -> f b

Monad (simplified):

class Monad m where
    (>>=) :: m a -> (a -> m b) -> m b
  • fmap takes a function and a functor, and returns a functor.
  • >>= takes a function and a monad, and returns a monad.

The difference between the two is in the function parameter:

  • fmap - (a -> b)
  • >>= - (a -> m b)

>>= takes a function parameter that returns a monad. I know that this is significant, but I'm having difficulty seeing how this one slight thing makes monads much more powerful than functors. Can someone explain?

Cactus
  • 27,075
  • 9
  • 69
  • 149
m0meni
  • 16,006
  • 16
  • 82
  • 141
  • 4
    this is easier seen with the flipped version of `(>>=)`, [`(=<<)`](https://stackoverflow.com/questions/34545818/is-monad-bind-operator-closer-to-function-composition-chaining-or-functi/34561605#34561605). With `(g <$>) :: f a -> f b`, the function `g :: a -> b` has no influence on the `f` "wrapping" -- doesn't change it. With `(k =<<) :: m a -> m b`, the function `k :: a -> m b` itself *creates* the new `m` "wrapping", so it can change. – Will Ness Feb 14 '16 at 01:11
  • @WillNess I can "understand" this, but I can't see it. I think the real problem I have is that I can't see what `>>=` can do that `fmap` can't do. In my head they're equivalent because I haven't seen an example, which shows that fmap is inadequate. – m0meni Feb 14 '16 at 01:16
  • 4
    going with lists, try to filter out some elements from a list, using `map`. you can't. but with `concatMap`, you can: `map (\x->x+1) [1,2,3]` vs `concatMap (\x-> [x,x+1|even x]) [1,2,3])`. – Will Ness Feb 14 '16 at 01:17
  • @WillNess okay I see! I always thought a `filter` operation was just a sugary variant of `map`, but just now I've realized that's not even possible. – m0meni Feb 14 '16 at 01:20
  • we can code *almost*-filter with `map (\x -> [x|even x]) [1,2,3]` but it produces `[[],[2],[]]` and another level of interpretation done by `concat` is then needed to make it really a `filter`. – Will Ness May 22 '17 at 19:26

3 Answers3

22

Well, (<$>) is an alias for fmap, and (=<<) is the same as (>>=) with the arguments swapped:

(<$>) :: (x ->   y) -> b x -> b y
(=<<) :: (x -> b y) -> b x -> b y

The difference is now fairly clear: with the bind function, we apply a function that returns a b y rather than a y. So what difference does that make?

Consider this small example:

foo <$> Just 3

Notice that (<$>) will apply foo to 3, and put the result back into a Just. In other words, the result of this computation cannot be Nothing. On the contrary:

bar =<< Just 3

This computation can return Nothing. (For example, bar x = Nothing will do it.)

We can do a similar thing with the list monad:

foo <$> [Red, Yellow, Blue]   -- Result is guaranteed to be a 3-element list.
bar =<< [Red, Yellow, Blue]   -- Result can be ANY size.

In short, with (<$>) (i.e., fmap), the "structure" of the result is always identical to the input. But with (=<<) (i.e., (>>=)), the structure of the result can change. This allows conditional execution, reacting to input, and a whole bunch of other things.

MathematicalOrchid
  • 61,854
  • 19
  • 123
  • 220
  • 6
    just for completeness, Applicative can return `Nothing` too: `Nothing <*> Just 3`. The difference is, the "piping" (i.e. computational structure) is *fixed* when the computation is composed, *before* it *"runs"*. But with Monads the piping can change depending on the values produced *while* it "runs". (in case of IO, `3` presumably is received e.g. as user's input). -- The *list* example is esp. good here: `(foo <$>)` keeps the structure (list's length); `([baz, quux] <*>)` will change the structure *predictably* (create length-6 list); with Monad all bets are off. – Will Ness Feb 14 '16 at 19:19
7

Short answer is that if you can turn m (m a) into m a in a way which makes sense then it's a Monad. This is possible for all Monads but not necessarily for Functors.

I think the most confusing thing is that all of the common examples of Functors (e.g. List, Maybe, IO) are also Monads. We need an example of something that is a Functor but not a Monad.

I'll use an example from a hypothetical calendar program. The following code defines an Event Functor which stores some data that goes with the event and the time that it occurs.

import Data.Time.LocalTime

data Event a = MkEvent LocalTime a

instance Functor Event where
    fmap f (MkEvent time a) = MkEvent time (f a)

The Event object stores the time that the event occurs and some extra data that can be changed using fmap. Now lets try and make it a Monad:

instance Monad Event where
    (>>=) (MkEvent timeA a) f = let (MkEvent timeB b) = f a in
                                MkEvent <notSureWhatToPutHere> b

We find that we can't because you will end up with two LocalTime objects. timeA from the given Event and timeB from the Event given by the result of f a. Our Event type is defined as having only one LocalTime (time) that it occurs at and so making it a Monad isn't possible without turning two LocalTimes into one. (There may be some case where doing so might make sense and so you could turn this into a monad if you really wanted to).

HEGX64
  • 221
  • 1
  • 9
  • 3
    One example of a classic/common functor that's not a monad is `newtype Const a b = Const a`. – dfeuer Feb 14 '16 at 06:52
  • 3
    `pure x >>= f` is required by a monad law to be `f x`, but `pure :: b -> Const a b` cannot possibly use its argument. – dfeuer Feb 14 '16 at 06:58
  • 1
    @dfeuer This seams [too simple to be simple](https://ncatlab.org/nlab/show/too+simple+to+be+simple). Also I can't find a way to write a Functor instance other than `fmap f (Const x) = Const x` – HEGX64 Feb 14 '16 at 08:01
  • 3
    @HEGX64: right. but for `Functor` this is no problem – in fact, it immediately guarantees the functor law `fmap id ≡ id`. – leftaroundabout Feb 14 '16 at 13:01
  • 1
    A less simple example: in FRP frameworks, events and signals tend to be functors, but not monads. This turns out to be important for efficient implementation. – dfeuer Feb 15 '16 at 07:37
3

Assume for a moment that IO were just a Functor, and not a Monad. How could we sequence two actions? Say, like getChar :: IO Char and putChar :: Char -> IO ().

We could try mapping over getChar (an action that, when executed, reads a Char from stdin) using putChar.

fmap putChar getChar :: IO (IO ())

Now we have a program that, when executed, reads a Char from stdin and produces a program that, when executed, writes the Char to stdout. But what we actually want is a program that, when executed, reads a Char from stdin and writes the Char to stdout. So we need a "flattening" (in the IO case, "sequencing") function with type:

join :: IO (IO ()) -> IO ()

Functor by itself does not provide this function. But it is a function of Monad, where it has the more general type:

join :: Monad m => m (m a) -> m a

What does all of this have to do with >>=? As it happens, monadic bind is just a combination of fmap and join:

:t \m f -> join (fmap f m)
(Monad m) => m a1 -> (a1 -> m a) -> m a

Another way of seeing the difference is that fmap never changes the overall structure of the mapped value, but join (and therefore >>= as well) can do that.

In terms of IO actions, fmap will never cause addicional reads/writes or other effects. But join sequences the read/writes of the inner action after those of the outer action.

danidiaz
  • 26,936
  • 4
  • 45
  • 95