Functors, Applicatives, And Monads In Pictures

Functors, Applicatives, And Monads In Pictures

原文:
http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
参考文章:
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
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:

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

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:

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

And fmap magically applies this function, because Maybe is a Functor. It specifies how fmapapplies to Justs and Nothings:

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

image

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

image
> fmap (+3) Nothing
Nothing

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?

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:

image

Here's a function applied to another function:

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:

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:

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]

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:

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:

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

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:

> 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:

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!

image

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

image

You can also chain these calls:

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

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
getLine :: IO String

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

image
readFile :: FilePath -> IO String

putStrLn takes a string and prints it:

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 >>=!

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, and a 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 <*> 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等等。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • rljs by sennchi Timeline of History Part One The Cognitiv...
    sennchi阅读 7,312评论 0 10
  • 念诗的男人最帅,吟诗的女人最美,勤奋的人儿最可爱。每天清晨一首诗,早安~
    苏苏21阅读 266评论 0 2
  • Q:选择做人流之前有没有纠结,具体是怎么想的 A:………………还是有点难以接受其实orz…不过我会零零碎碎说,我慢...
    Zmirror阅读 316评论 0 0
  • 远远的从远处望去,就看到一个人慢慢的从那边走过来.穿着米黄色的皮衣,里面穿的一个牛仔短裤,上身穿着白t血。在走廊的...
    不像话的故事阅读 253评论 0 0
  • 近日,又一部大火的韩剧《Go Back夫妇》席卷全国,引发了大家的高度关注。除了剧情本身的幽默诙谐外,本剧的女主演...
    侃侃网剧阅读 368评论 0 0