26

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.8k10 gold badges74 silver badges165 bronze badges
asked Feb 14, 2016 at 1:04
5
  • 4
    this is easier seen with the flipped version of (>>=), (=<<). 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. Commented Feb 14, 2016 at 1: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. Commented Feb 14, 2016 at 1: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]). Commented Feb 14, 2016 at 1: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. Commented Feb 14, 2016 at 1: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. Commented May 22, 2017 at 19:26

3 Answers 3

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.

answered Feb 14, 2016 at 13:39
Sign up to request clarification or add additional context in comments.

1 Comment

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.
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).

answered Feb 14, 2016 at 4:06

5 Comments

One example of a classic/common functor that's not a monad is newtype Const a b = Const a.
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 This seams 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: right. but for Functor this is no problem – in fact, it immediately guarantees the functor law fmap id ≡ id.
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.
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.

answered Feb 14, 2016 at 10:39

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.