参考文章:
​http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf​


Here's a simple value:




Functors, Applicatives, And Monads In Pictures_swift


image

And we know how to apply a function to this value:


Functors, Applicatives, And Monads In Pictures_sed_02


image


Simple enough. Lets extend this by saying that any value can be in a context. For now you can think of a context as a box that you can put a value in:



Functors, Applicatives, And Monads In Pictures_ide_03


image


Now when you apply a function to this value, you'll get different results depending on the context. This is the idea that Functors, Applicatives, Monads, Arrows etc are all based on. The ​​Maybe​​ data type defines two related contexts:



Functors, Applicatives, And Monads In Pictures_ide_04


image


data Maybe a = Nothing | Just a

In a second we'll see how function application is different when something is a ​​Just a​​​ versus a ​​Nothing​​. First let's talk about Functors!

Functors

When a value is wrapped in a context, you can't apply a normal function to it:



Functors, Applicatives, And Monads In Pictures_ide_05


image


This is where ​​fmap​​​ comes in. ​​fmap​​​ is from the street, ​​fmap​​​ is hip to contexts. ​​fmap​​​ knows how to apply functions to values that are wrapped in a context. For example, suppose you want to apply ​​(+3)​​​ to ​​Just 2​​​. Use ​​fmap​​:

> fmap (+3) (Just 2)
Just 5



Functors, Applicatives, And Monads In Pictures_swift_06


image


Bam!​fmap​​​ shows us how it's done! But how does ​​fmap​​ know how to apply the function?

Just what is a Functor, really?

​Functor​​​ is a ​​typeclass​​. Here's the definition:



Functors, Applicatives, And Monads In Pictures_sed_07


image


A ​​Functor​​​ is any data type that defines how ​​fmap​​​ applies to it. Here's how ​​fmap​​ works:



Functors, Applicatives, And Monads In Pictures_ide_08


image


So we can do this:

> fmap (+3) (Just 2)
Just 5

And ​​fmap​​​ magically applies this function, because ​​Maybe​​​ is a Functor. It specifies how ​​fmap​​​applies to ​​Just​​​s and ​​Nothing​​s:

instance Functor Maybe where
fmap func (Just val) = Just (func val)
fmap func Nothing = Nothing

Here's what is happening behind the scenes when we write ​​fmap (+3) (Just 2)​​:



Functors, Applicatives, And Monads In Pictures_sed_09


image


So then you're like, alright ​​fmap​​​, please apply ​​(+3)​​​ to a ​​Nothing​​?



Functors, Applicatives, And Monads In Pictures_ide_10


image


> fmap (+3) Nothing
Nothing



Functors, Applicatives, And Monads In Pictures_sed_11


Bill O'Reilly being totally ignorant about the Maybe functor


Like Morpheus in the Matrix, ​​fmap​​​ knows just what to do; you start with ​​Nothing​​​, and you end up with ​​Nothing​​​! ​​fmap​​​ is zen. Now it makes sense why the ​​Maybe​​​ data type exists. For example, here's how you work with a database record in a language without ​​Maybe​​:

post = Post.find_by_id(1)
if post
return post.title
else
return nil
end

But in Haskell:

fmap (getPostTitle) (findPost 1)

If ​​findPost​​​ returns a post, we will get the title with ​​getPostTitle​​​. If it returns ​​Nothing​​​, we will return ​​Nothing​​​! Pretty neat, huh? ​​<$>​​​ is the infix version of ​​fmap​​, so you will often see this instead:

getPostTitle <$> (findPost 1)

Here's another example: what happens when you apply a function to a list?



Functors, Applicatives, And Monads In Pictures_swift_12


image


Lists are functors too! Here's the definition:

instance Functor [] where
fmap = map

Okay, okay, one last example: what happens when you apply a function to another function?

fmap (+3) (+1)

Here's a function:



Functors, Applicatives, And Monads In Pictures_ide_13


image


Here's a function applied to another function:



Functors, Applicatives, And Monads In Pictures_sed_14


image


The result is just another function!

> import Control.Applicative
> let foo = fmap (+3) (+2)
> foo 10
15

So functions are Functors too!

instance Functor ((->) r) where
fmap f g = f . g

When you use fmap on a function, you're just doing function composition!

Applicatives

Applicatives take it to the next level. With an applicative, our values are wrapped in a context, just like Functors:



Functors, Applicatives, And Monads In Pictures_sed_15


image


But our functions are wrapped in a context too!



Functors, Applicatives, And Monads In Pictures_swift_16


image


Yeah. Let that sink in. Applicatives don't kid around. ​​Control.Applicative​​​ defines ​​<*>​​, which knows how to apply a function wrapped in a context to a value wrapped in a context:



Functors, Applicatives, And Monads In Pictures_swift_17


image


i.e:

Just (+3) <*> Just 2 == Just 5

Using ​​<*>​​ can lead to some interesting situations. For example:

> [(*2), (+3)] <*> [1, 2, 3]
[2, 4, 6, 4, 5, 6]



Functors, Applicatives, And Monads In Pictures_ide_18


image


Here's something you can do with Applicatives that you can't do with Functors. How do you apply a function that takes two arguments to two wrapped values?

> (+) <$> (Just 5)
Just (+5)
> Just (+5) <$> (Just 4)
ERROR ??? WHAT DOES THIS EVEN MEAN WHY IS THE FUNCTION WRAPPED IN A JUST

Applicatives:

> (+) <$> (Just 5)
Just (+5)
> Just (+5) <*> (Just 3)
Just 8

​Applicative​​​ pushes ​​Functor​​​ aside. "Big boys can use functions with any number of arguments," it says. "Armed ​​<$>​​​ and ​​<*>​​, I can take any function that expects any number of unwrapped values. Then I pass it all wrapped values, and I get a wrapped value out! AHAHAHAHAH!"

> (*) <$> Just 5 <*> Just 3
Just 15

And hey! There's a function called ​​liftA2​​ that does the same thing:

> liftA2 (*) (Just 5) (Just 3)
Just 15

Monads

How to learn about Monads:

  1. Get a PhD in computer science.
  2. Throw it away because you don't need it for this section!

Monads add a new twist.

Functors apply a function to a wrapped value:



Functors, Applicatives, And Monads In Pictures_sed_19


image


Applicatives apply a wrapped function to a wrapped value:



Functors, Applicatives, And Monads In Pictures_ide_20


image


Monads apply a function that returns a wrapped value to a wrapped value. Monads have a function ​​>>=​​ (pronounced "bind") to do this.

Let's see an example. Good ol' ​​Maybe​​ is a monad:



Functors, Applicatives, And Monads In Pictures_swift_21


Just a monad hanging out


Suppose ​​half​​ is a function that only works on even numbers:

half x = if even x
then Just (x `div` 2)
else Nothing



Functors, Applicatives, And Monads In Pictures_sed_22


image


What if we feed it a wrapped value?



Functors, Applicatives, And Monads In Pictures_ide_23


image


We need to use ​​>>=​​​ to shove our wrapped value into the function. Here's a photo of ​​>>=​​:



Functors, Applicatives, And Monads In Pictures_sed_24


image


Here's how it works:

> Just 3 >>= half
Nothing
> Just 4 >>= half
Just 2
> Nothing >>= half
Nothing

What's happening inside? ​​Monad​​ is another typeclass. Here's a partial definition:

class Monad m where
(>>=) :: m a -> (a -> m b) -> m b

Where ​​>>=​​ is:



Functors, Applicatives, And Monads In Pictures_swift_25


image


So ​​Maybe​​ is a Monad:

instance Monad Maybe where
Nothing >>= func = Nothing
Just val >>= func = func val

Here it is in action with a ​​Just 3​​!



Functors, Applicatives, And Monads In Pictures_sed_26


image


And if you pass in a ​​Nothing​​ it's even simpler:



Functors, Applicatives, And Monads In Pictures_sed_27


image


You can also chain these calls:

> Just 20 >>= half >>= half >>= half
Nothing



Functors, Applicatives, And Monads In Pictures_swift_28


image



Functors, Applicatives, And Monads In Pictures_sed_29


image


Cool stuff! So now we know that ​​Maybe​​​ is a ​​Functor​​​, an ​​Applicative​​​, and a ​​Monad​​.

Now let's mosey on over to another example: the ​​IO​​ monad:



Functors, Applicatives, And Monads In Pictures_ide_30


image


Specifically three functions. ​​getLine​​ takes no arguments and gets user input:



Functors, Applicatives, And Monads In Pictures_swift_31


image


getLine :: IO String

​readFile​​ takes a string (a filename) and returns that file's contents:



Functors, Applicatives, And Monads In Pictures_ide_32


image


readFile :: FilePath -> IO String

​putStrLn​​ takes a string and prints it:



Functors, Applicatives, And Monads In Pictures_swift_33


image


putStrLn :: String -> IO ()

All three functions take a regular value (or no value) and return a wrapped value. We can chain all of these using ​​>>=​​!



Functors, Applicatives, And Monads In Pictures_ide_34


image


getLine >>= readFile >>= putStrLn

Aw yeah! Front row seats to the monad show!

Haskell also provides us with some syntactical sugar for monads, called ​​do​​ notation:

foo = do
filename <- getLine
contents <- readFile filename
putStrLn contents

Conclusion

  1. A functor is a data type that implements the​​Functor​​ typeclass.
  2. An applicative is a data type that implements the​​Applicative​​ typeclass.
  3. A monad is a data type that implements the​​Monad​​ typeclass.
  4. A​​Maybe​​ implements all three, so it is a functor, an applicative,anda monad.

What is the difference between the three?



Functors, Applicatives, And Monads In Pictures_swift_35


image


  • functors:you apply a function to a wrapped value using​​fmap​​​ or​​<$>​
  • applicatives:you apply a wrapped function to a wrapped value using​​<*>​​​ or​​liftA​
  • monads:you apply a function that returns a wrapped value, to a wrapped value using​​>>=​​​ or​​liftM​

So, dear friend (I think we are friends by this point), I think we both agree that monads are easy and a SMART IDEA(tm). Now that you've wet your whistle on this guide, why not pull a Mel Gibson and grab the whole bottle. Check out LYAH's ​​section on Monads​​. There's a lot of things I've glossed over because Miran does a great job going in-depth with this stuff.

Translations

This post has been translated into:

Human languages:

Programming languages:

If you translate this post, ​​send me an email​​ and I'll add it to this list!

For more monads and pictures, check out ​​three useful monads​​.

FP , 又称为 Monadic Programming , 泛函编程。

不同类型的Monad实例则会支持不同的程序运算行为,如:Option Monad在运算中如果遇到None值则会中途退出;State Monad会确保状态值会伴随着程序运行流程直到终结;List Monad运算可能会产生多个结果等等。Scalaz提供了很多不同种类的Monad如:StateMonad, IOMonad, ReaderMonad, WriterMonad,MonadTransformer等等。