Difference in opportunities between fmap and bind?

I am new to functional programming (from javascript), and it's hard for me to talk about the difference between the two, which is also related to my understanding of functors and monads.

Functor

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

Monad (simplified):

 class Monad m where (>>=) :: ma -> (a -> mb) -> mb 
  • 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 -> mb)

>>= takes a function parameter that returns a monad. I know this is important, but itโ€™s hard for me to understand how this little thing makes monads much more powerful than functors. Can someone explain?

+8
functor functional-programming haskell monads
source share
3 answers

Well, (<$>) is an alias for fmap , and (=<<) matches (>>=) with replaced arguments:

 (<$>) :: (x -> y) -> bx -> by (=<<) :: (x -> by) -> bx -> by 

Now the difference is pretty obvious: with the bind function, we use a function that returns by , not y . So what's the difference?

Consider this small example:

 foo <$> Just 3 

Note that (<$>) apply foo to 3 and return the result to Just . In other words, the result of this calculation cannot be Nothing . On the contrary:

 bar =<< Just 3 

This calculation may return Nothing . (For example, bar x = Nothing will do this.)

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, response to input, and a whole bunch of other things.

+13
source share

The short answer is that if you can turn m (ma) into ma in a way that makes sense, then this is Monad. This is possible for all Monads, but not required for Functors.

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

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

 import Data.Time.LocalTime data Event a = MkEvent LocalTime a instance Functor Event where fmap f (MkEvent time a) = MkEvent time (fa) 

The Event object stores the time during which the event occurs, and some additional data that can be changed using fmap . Now try to make Monad:

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

We find that we cannot, because you will have two LocalTime . timeA from Event data and timeB from Event specified by fa . Our Event type is defined as having only one LocalTime ( time ) in which it occurs, and therefore it is impossible to make it Monad without turning two LocalTime into one. (Maybe this is a case where it might make sense, and so you can turn it into a monad if you really want to.)

+8
source share

Suppose for a moment that the IO were just a Functor , not a Monad . How can we do two things? Let's say getChar :: IO Char and putChar :: Char -> IO () .

We could try matching on getChar (an action that reads Char from stdin when executed) using putChar .

 fmap putChar getChar :: IO (IO ()) 

Now we have a program that, when executed, reads Char from stdin and creates a program that, when executed, writes Char to stdout. But what we really want is a program that, when executed, reads Char from stdin and writes Char to stdout. Therefore, we need a โ€œflatteningโ€ (in the case of an IO โ€œsequenceโ€) with a type:

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

Functor alone does not provide this feature. But this is a function from Monad , where it has a more general type:

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

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

 :t \mf -> join (fmap fm) (Monad m) => m a1 -> (a1 -> ma) -> ma 

Another way to see the difference is that fmap never changes the overall structure of the displayed value, but join (and therefore >>= ) can do this.

In terms of IO actions, fmap will never cause additional reads / writes or other effects. But join does the read / write sequence of the internal action after the external action.

+3
source share

All Articles