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
fmaptakes 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?
3 Answers 3
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.
1 Comment
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.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).
5 Comments
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.fmap f (Const x) = Const xFunctor this is no problem – in fact, it immediately guarantees the functor law fmap id ≡ id.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.
Comments
Explore related questions
See similar questions with these tags.
(>>=),(=<<). With(g <$>) :: f a -> f b, the functiong :: a -> bhas no influence on thef"wrapping" -- doesn't change it. With(k =<<) :: m a -> m b, the functionk :: a -> m bitself creates the newm"wrapping", so it can change.>>=can do thatfmapcan't do. In my head they're equivalent because I haven't seen an example, which shows that fmap is inadequate.map. you can't. but withconcatMap, you can:map (\x->x+1) [1,2,3]vsconcatMap (\x-> [x,x+1|even x]) [1,2,3]).filteroperation was just a sugary variant ofmap, but just now I've realized that's not even possible.map (\x -> [x|even x]) [1,2,3]but it produces[[],[2],[]]and another level of interpretation done byconcatis then needed to make it really afilter.