Functors, Applicatives, And Monads 图解
来源:互联网 发布:砍柴网源码 编辑:程序博客网 时间:2024/05/21 22:38
PS:这篇文章是Haskell语言写的,所以很多API会不清楚,我们只是学习这种思想这里现在有一个简单的值:
我们知道如何将函数应用于这个值:
很简单。让我们扩展一下,来说明任何值都可以在上下文中。现在,你可以把上下文想象成一个盒子,你可以把值放在里面:
现在,当您将一个函数应用到这个值时,您将根据上下文得到不同的结果。Functors、Applicatives, Monads, Arrows等都是基于此的思想。Maybe数据类型定义了两个相关的上下文:
很快我们将会看到:一个JUST A与之相对的另一个Nothing时候,函数应用有何不同的。首先让我们来谈谈Functors吧!PS;这里需要注意一下,因为这篇文章作者是kaskell写的,很多东西是您所熟悉的语言没有。解释一下。Maybe其实就是java与Scala中的Option,或者是javaScript的Promise,Swift中的Optional ,它就是我们在本文章中提到了容器,也可以叫“黑盒”,我们将值放进去,等于为该值定义了一个上下文环境。
1.Functors
当一个值被包装在一个上下文中,你不能对它应用一个正常的函数:
这就是fmap的切入点(注意。fmap是haskell的语法,我们还总结javaScript的语法,在javaScript中对应的就是map)。fmap是在街道上的,fmap熟悉上下文环境。fmap知道如何将函数应用到封装在上下文中的值。你可以对任何一个类型为 Functor 的类型使用 fmap.例如,假设您想要应用(+3)到Just 2上。如何使用fmap:
//hasKell>fmap (+3) (Just 2)Just 5//python 要先做准备工作$ pip3 install oslashpython3>>> from oslash import *>>> Just(2).map(lambda x: x+3)Just 5
Bam!fmap向我们展示它是怎么做的!但是fmap是怎么知道如何去应用函数的呢??
1.1 什么是Functor?
Functor是一个typeclass.这里是它的定义:
一个Functor可以是任何定义了fmap如何应用的数据类型,下面是fmap如何工作的:
所以我们可以这样做:
> fmap (+3) (Just 2)Just 5
fmap神奇地应用了这个函数,因为Maybe是一个Functor。它列明了如何可应用于Just和Nothing:
//haskell的fmapinstance Functor Maybe where fmap func (Just val) = Just (func val) fmap func Nothing = Nothing//下面的是javaScript的map的定义_.map = _.collect = function(obj, iteratee, context) { iteratee = cb(iteratee, context); var keys = !isArrayLike(obj) && _.keys(obj), length = (keys || obj).length, results = Array(length); for (var index = 0; index < length; index++) { var currentKey = keys ? keys[index] : index; results[index] = iteratee(obj[currentKey], currentKey, obj); } return results;};
以下是我们写fmap (+3) (Just 2)的情况背后发生的事情:
所以你可以随你喜欢,fmap都是没问题的。你说为Nothing应用(+3)??
//haskell> fmap (+3) NothingNothing
就像《黑客帝国》中的莫菲斯.fmap知道应该做什么;你以Nothing开始,那么最终也是Nothing来结束!!现在可以理解为什么Maybe数据类型存在的意义了。例如,这里是你在一个没有Maybe的语言中与你操作数据库记录的方法:
post = Post.find_by_id(1)if post return post.titleelse return nilend
但是在Haskell中:
fmap (getPostTitle) (findPost 1)
如果findPost返回了一个post,我们将会通过getPostTitle来获取其标题。但是如果返回的是Nothing,那么我们最终也将会返回Nothing(我们刚才的实例有讲过)。这很优雅,不是吗(是挺优雅的,记不住呀)??<$>是fmap的中缀表达式版本,所以你经常会看到这样:
getPostTitle <$> (findPost 1)
下面是另一个例子:当您将一个函数应用到一个列表时会发生什么?
List仅仅是另一种让fmap以不同方式应用函数的上下文!列表也是Functor,这里是它的定义:
instance Functor [] where fmap = map
好的,最后一个例子,当你将一个函数应用到另一个函数时会发生什么?
fmap (+3) (+1)
这里是一个函数
这是一个应用于另一个函数的函数:
结果是另外一个函数(果然函数语言不同呀,我还以为要报错呢,有点像Scala的函数字面量)!!
> import Control.Applicative> let foo = fmap (+3) (+2)> foo 1015
因此,函数也是Functor
instance Functor ((->) r) where fmap f g = f . g
当你在函数上使用fmap时,你只是在做函数复合(就是函数的组合呗)!
2. Applicatives
Applicatives将它带到下一个层次。在Applicatives中,我们的值被包装在一个上下文中,就像Functors一样:
但我们的函数也被包装在一个上下文中!
好的,让我们来理解一下它。Applicatives没有开玩笑。Control.Applicative定义了<*>,它知道如何将一个封装在上下文中的函数应用到一个上下文中的值:
i.e:
Just (+3) <*> Just 2 == Just 5
使用<*>可以导致一些有趣的状况。比如:
> [(*2), (+3)] <*> [1, 2, 3][2, 4, 6, 4, 5, 6]
这里有一些是你能用 Applicative 做, 而无法用 Functor 做到的。你怎么才能把需要两个参数的函数应用到两个封装的值(就是我们刚才看到的包装在上下文中的值)上呢?
> (+) <$> (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将Functors推到一边,她说“大男孩可以以任意数量的参数来使用函数”。“有了 <$>和<*>,我可以取任意数量的未包装的参数值的函数。然后我把所有的包值都传递给它,然后得到一个包装的值。AHAHAHAHAH”
> (*) <$> Just 5 <*> Just 3Just 15
嘿!这里就有一个叫做liftA2的函数做了这样xiangtong 的事情
> liftA2 (*) (Just 5) (Just 3)Just 15
3. Monads
如何了解Monads:1.获得计算机科学博士学位2.把它扔掉,因为你不需要它!Monads增加了一个新的转折。Functors将函数应用于包装的值:
Applicatives将包装的函数应用于包装的值:
Monads为包装的值应用一个返回包装值的函数。Monads有一个函数>>=来做处理。让我们看一个例子。
假设half是一个只适用于偶数的函数:
half x = if even x then Just (x `div` 2) else Nothing
如果我们给它一个包装的值呢?
我们需要使用>>=将包装的值推入函数中。这是>>=的照片
它是如何工作的:
> Just 3 >>= halfNothing> Just 4 >>= halfJust 2> Nothing >>= halfNothing
内部发生了什么?Monads是另一个typeclass。这是一个部分的定义:
class Monad m where (>>=) :: m a -> (a -> m b) -> m b
>>=在哪里:
因此,Maybe是一个Monads
instance Monad Maybe where Nothing >>= func = Nothing Just val >>= func = func val
下面是我们使用Just 3所发生的事:
如果你传递一个Nothiong,它更简单:
你也可以把这些调用串起来:
> Just 20 >>= half >>= half >>= halfNothing
Cool(我咋觉得作者已经疯了)!!因此现在我们知道了Maybe是一个Functors,也是一个Applicative,还是一个Monad。现在让我们来看看另一个例子:IO monad:
明确的三个函数:getLine不接受参数,获取用户输入:
getLine :: IO String
readFile 获取一个字符串(一个文件名)并返回该文件的内容:
readFile :: FilePath -> IO String
putStrLn取一个字符串并打印它:
putStrLn :: String -> IO ()
这三个函数都具有一个常规值(或没有值),并返回一个包装的值。我们可以用>>=将他们穿起来 !
getLine >>= readFile >>= putStrLn
Haskell还为我们提供了一些关于monads的语法糖,叫做do表示法:
foo = do filename <- getLine contents <- readFile filename putStrLn contents
4. 结论
1.一个functor就是一个实现了Functor typeclass的数据类型2.一个applicative 就是一个实现了Applicative typeclass的数据类型3.一个monad就是一个实现了Monad typeclass的数据类型4.Maybe是Functor ,Applicative ,它还是Monad 这三者的区别在哪??
functors:您可以使用fmap或<$>将函数应用于包装值applicatives:您可以使用<*>或liftA将一个包装的函数应用到包装的值monads:通过使用>>=或liftM为为包装的值应用一个函数,该函数返回一个包装的值所以,亲爱的朋友(我认为我们是朋友),我认为我们都同意monads 是很容易的,也是一个聪明的想法(tm)。
阅读全文
0 0
- Functors, Applicatives, And Monads 图解
- Scala函数式程序设计 week1 For Expressions and Monads
- 泛化仿函数(functors)
- 仿函数(functors)
- 七 仿函数 functors
- Understanding Haskell Monads
- Monads之仿函数
- Monads之flatMap,unit
- Monads之关系选择器
- Monads之规则
- Monads之作用
- 图解iptables:chains and tables
- Function Pointers, Functors, Virtual Functions
- 仿函数(functors)使用
- Monads are Elephants Part 4
- 图解extends and implements 的区别
- 图解NIO(2) Selector and 聊天室
- Callbacks In C++ Using Template Functors
- OJ 2552: 好好学习天天向上
- hdu 1024 最大上升子序列
- java的properties文件-jdbc优化编程(五)
- IO流
- Android之获取移动网络ip
- Functors, Applicatives, And Monads 图解
- 0906 JSP标准标签库JSTL
- myeclipse 10卡死强退后再打开就卡在 loading workbench解决方法
- Path of Equal Weight
- eplise 连接谷歌服务器
- Dynamic Shadow Projector
- HDU5402 Travelling Salesman Problem 【模拟】
- 自我介绍以及对于软件工程的看法
- Unity 2017 内网环境 项目选择页白屏问题