参考文章:
http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf
Here's a simple value:
image
And we know how to apply a function to this value:
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:
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:
image
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:
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
:
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:
image
A Functor
is any data type that defines how fmap
applies to it. Here's how fmap
works:
image
So we can do this:
And fmap
magically applies this function, because Maybe
is a Functor. It specifies how fmap
applies to Just
s and Nothing
s:
Here's what is happening behind the scenes when we write fmap (+3) (Just 2)
:
image
So then you're like, alright fmap
, please apply (+3)
to a Nothing
?
image
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
:
But in Haskell:
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:
Here's another example: what happens when you apply a function to a list?
image
Lists are functors too! Here's the definition:
Okay, okay, one last example: what happens when you apply a function to another function?
Here's a function:
image
Here's a function applied to another function:
image
The result is just another function!
So functions are Functors too!
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:
image
But our functions are wrapped in a context too!
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:
image
i.e:
Using <*>
can lead to some interesting situations. For example:
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?
Applicatives:
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!"
And hey! There's a function called liftA2
that does the same thing:
Monads
How to learn about Monads:
- Get a PhD in computer science.
- 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:
image
Applicatives apply a wrapped function to a wrapped value:
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:
Just a monad hanging out
Suppose half
is a function that only works on even numbers:
image
What if we feed it a wrapped value?
image
We need to use >>=
to shove our wrapped value into the function. Here's a photo of >>=
:
image
Here's how it works:
What's happening inside? Monad
is another typeclass. Here's a partial definition:
Where >>=
is:
image
So Maybe
is a Monad:
Here it is in action with a Just 3
!
image
And if you pass in a Nothing
it's even simpler:
image
You can also chain these calls:
image
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:
image
Specifically three functions. getLine
takes no arguments and gets user input:
image
readFile
takes a string (a filename) and returns that file's contents:
image
putStrLn
takes a string and prints it:
image
All three functions take a regular value (or no value) and return a wrapped value. We can chain all of these using >>=
!
image
Aw yeah! Front row seats to the monad show!
Haskell also provides us with some syntactical sugar for monads, called do
notation:
Conclusion
- A functor is a data type that implements the
Functor
typeclass. - An applicative is a data type that implements the
Applicative
typeclass. - A monad is a data type that implements the
Monad
typeclass. - A
Maybe
implements all three, so it is a functor, an applicative,anda monad.
What is the difference between the three?
image
- functors:you apply a function to a wrapped value using
fmap
or<$>
- applicatives:you apply a wrapped function to a wrapped value using
<*>
orliftA
- monads:you apply a function that returns a wrapped value, to a wrapped value using
>>=
orliftM
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:
- Chinese
- Another Chinese translation
- Chinese, Kotlin
- French
- German
- Japanese
- Korean
- Portuguese
- Russian
- Spanish
- Turkish
- Vietnamese
Programming languages:
- Javascript
- Python
- Swift
- Kotlin. This author also translated this Kotlin version intoChinese.
- Kotlin (translated from the Swift translation)
- Elm
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等等。