在函数式编程基础[一]–范畴论和Functor 中,我们了解了什么是Category和Functors,现在我们继续往下看Applicative Functor。
先快速回顾一下Functor,我们知道Maybe Functor可以帮我们实现下面的运算:
|
|
但是我们如何计算两个Maybe值的和呢?比如 Just(3) + Just(2)
. 这就是Applicative要帮我们解决的问题。
Applicative 定义
Applicative由三部分组成:
- 一个类型构造函数f –例如 List、Maybe
- 一个函数pure : a -> f a,其中a可以是任何类型(包括函数类型)
- 一个函数apply(<*>) : f (a -> b) -> f a -> f b,其中a和b可以是任何类型(包括函数类型)
具体定义如下:
|
|
pure
本质上,pure
函数是一个构造函数,可以将任何类型a的值转换为f a的值,pure将其参数 “提升(lifts)/注入(inject) “到类型构造函数所定义的相应类型中(例如从Int到List Int)。我们来看看Maybe的实现:
|
|
这里pure
就是把值用Just
包起来:
因为a可以是任意类型,我们当然也可以包一个函数:
顺便看一下List的实现:
|
|
apply(<*>)
Apply函数接收一个嵌入到类型构造函数定义的上下文中的函数(如 Maybe(a -> b)
),并将其提升/转换为类型构造函数领域中的函数(Maybe a → Maybe b)。
回到最开始那个问题:如何计算两个Maybe值的,我们可以利用apply方法:
|
|
apply函数接收一个单个参数的函数(a -> b)
,如果我们给它传一个多参数的方法会怎么样呢?
a1 -> a2 -> ... -> an
如果我们把这个函数f传给pure
,会得到一个被f封装的多参数方法:f (a1 -> a2 -> ... -> an)
。
再将它传给apply
就可以得到:
f a1 -> f (a2 -> ... -> an)
我们可以重复使用apply
函数将其转换为f a1 -> f a2 -> ... -> f an
。
我们可以通过这种方式将多参数的函数提升到Applicative中,如图:
我们用一个两个参数的函数举例:(+) :: Num a => a -> a -> a
|
|
Applicative Laws
和Functor一样,Applicative也需要满足一些laws:
|
|
这里非常有趣,我们怎么实现才能满足这些laws呢?一般是选择最普通,最平常,最没有特色,最直观的方式实现pure
函数。比如对于List Applicative,pure
是这样实现的:
|
|
我们当然可以实现成 [x, x],但是就可能会不符合某些laws了。
Conclusion
当我们看到Applicative的时候,可以联想到context,wrapper,box这些概念。我们将某些数据封装到盒子中,然后将针对盒子中具体数据的函数也打包到盒子里,两个盒子相互作用,从而得到一个带有新值的新盒子。