最近在阅读了《Haskell趣学指南》后对函数式编程有了更加深入的认识。其中 functor 、applicative 和 monad 这一系列的概念是Haskell中重要的组成部分。写一下读后感算是复习一下书中的内容。
Functor(函子)
在 Haskell 中 functor 代表可以映射的事物。可以映射的事物比如说是[Int](整型数组),swift中的optional类型,字典,Set 等。概括起来凡是拥有容器性质的类型都可以视作 functor(函子)。在 Haskell 中 functor 是一个类型类(相当于 oc 中的协议,Java 中的接口)。在 functor 类型类中只定义了一个方法。
fmap :: (a -> b) -> f a -> f b
解释一下 fmap
函数。 fmap
函数接受两个参数 (a -> b) 和 f a 。其中
(a -> b) 参数是一个函数。这个函数接受 a 类型,返回 b 类型
f a 参数是一个容器类型,然后容器里面装的类型是 a。
f b 是 fmap 函数的返回值,是一个容器类型,然后容器类型里面装的是 b。
举一个把具体的类型应用到 fmap 函数的定义里面的例子:
fmap :: (Int -> String) -> [Int] -> [String]
对于这个例子,fmap 函数的作用就是接受一个把 Int 映射到 String 的函数,然后把这个函数喂给 Int 数组([Int]),吧这个 Int 数组变成 String 数组([String])。
做个比喻,fmap 函数做的事情就是相当于,吧一个装着 a 类型的盒子,打开然后把里面的 a 类型拿出来变成 b 类型,之后在返回原来的盒子。
Applicative
Applicative 是一种加强的 functor 。
Applicative 为什么存在?
首先我们可以把函子值是看做有上下文的值。
用盒子模型来解释的话,盒子里面的 a 就是函子值,而装着 a 的盒子就是 a 的上下文。所以 fmap 函数是在保持上下文不变的条件下,把一个函数应用到值上面。
上下文(也就是盒子)是什么东西都可以装的,也就是说上下文(盒子)里面也是可以装函数的。比如
那么我们怎么办吧这个盒子里的函数应用到包装在其他盒子里的值呢?这里就是 applicative 应用的地方了。
Applicative 定义
在 haskell 中想要是 Applicative 类型类的实例的前提是已经是 functor 类型类的实例。
在 Applicative 的类型类的定义中有两个方法:
pure :: a -> f a
(<*>) :: f (a -> b) -> f a -> f b
pure
函数:接受一个类型 a 返回一个把这个 a 放入上下文(盒子)中的类型。意思就是把 a 打包成具有上下文的类型。用盒子模型来理解就是把 a 装入一个盒子里面。<*>
函数:接受一个具有上下文的函数(a -> b)和一个具有上下文的 a (就是 f a)为参数,然后返回具有上下文的 b (就是 f b)的值。
用盒子模型来理解 <*> 函数的意思就是:
分别把函数(a -> b)和参数( a )从盒子中取出来,然后把函数应用到参数上,吧得到的值再装回盒子里面。
Monad
Monad 是升级的 applicative。在 applicative 的基础上支持绑定。但是在 Haskell 中 Monad 和 applicatvie 在代码上并没有联系,因为根据在 Haskell 中是先出现 Monad 然后才出现 applicative 的。
首先来看 Monad 的定义
return :: a -> m a
(>>=) :: m a -> (a -> m b) -> m b
(>>) :: m a -> m b -> m b
fail :: String -> m a
在 monad 类型类里面总共定义了4个函数:
return
函数:就是把 a 放入一个上下文中。和 applicative 里面的 pure 函数的作用一样>>
函数:这个函数特别简单,就是无条件的把第二个参数作为返回值返回fail
函数:这个函数是由 Haskell 来调用,我们永远都不会显示的调用>>=
函数:这个函数就是上面所说的绑定。他接受一个带有上下文的值和一个返回具有上下文的值的函数为参数,返回最后参数的函数中的返回值。
用盒子模型来解释绑定
绑定的过程就是首先把 a 从盒子中取出来,然后把 a 作用到函数,改函数直接返回了一个装有 b 的盒子。