正文
return
x
+
y
;
}
add
(
1
,
2
)
// 3
// 柯里化之后
function
addX
(
y
)
{
return
function
(
x
)
{
return
x
+
y
;
};
}
addX
(
2
)(
1
)
// 3
有了柯里化以后,我们就能做到,所有函数只接受一个参数。后文的内容除非另有说明,都默认函数只有一个参数,就是所要处理的那个值。
三、函子
函数不仅可以用于同一个范畴之中值的转换,还可以用于将一个范畴转成另一个范畴。这就涉及到了函子(Functor)。
3.1 函子的概念
函子是函数式编程里面最重要的数据类型,也是基本的运算单位和功能单位。
它首先是一种范畴,也就是说,是一个容器,包含了值和变形关系。比较特殊的是,它的变形关系可以依次作用于每一个值,将当前容器变形成另一个容器。
上图中,左侧的圆圈就是一个函子,表示人名的范畴。外部传入函数f,会转成右边表示早餐的范畴。
下面是一张更一般的图。
上图中,函数f完成值的转换(a到b),将它传入函子,就可以实现范畴的转换(Fa到Fb)。
3.2 函子的代码实现
任何具有map方法的数据结构,都可以当作函子的实现。
class Functor {
constructor(val) {
this.val = val;
}
map(f) {
return new Functor(f(this.val));
}
}
上面代码中,Functor是一个函子,它的map方法接受函数f作为参数,然后返回一个新的函子,里面包含的值是被f处理过的(f(this.val))。
一般约定,函子的标志就是容器具有map方法。该方法将容器里面的每一个值,映射到另一个容器。
下面是一些用法的示例。
(new Functor(2)).map(function (two) {
return two + 2;
});
// Functor(4)
(new Functor('flamethrowers')).map(function(s) {
return s.toUpperCase();
});
// Functor('FLAMETHROWERS')
(new Functor('bombs')).map(_.concat(' away')).map(_.prop('length'));
// Functor(10)
上面的例子说明,函数式编程里面的运算,都是通过函子完成,即运算不直接针对值,而是针对这个值的容器—-函子。函子本身具有对外接口(map方法),各种函数就是运算符,通过接口接入容器,引发容器里面的值的变形。
因此,学习函数式编程,实际上就是学习函子的各种运算。由于可以把运算方法封装在函子里面,所以又衍生出各种不同类型的函子,有多少种运算,就有多少种函子。函数式编程就变成了运用不同的函子,解决实际问题。
四、of 方法
你可能注意到了,上面生成新的函子的时候,用了new命令。这实在太不像函数式编程了,因为new命令是面向对象编程的标志。
函数式编程一般约定,函子有一个of方法,用来生成新的容器。
下面就用of方法替换掉new。
Functor.of = function(val) {
return new Functor(val);
};
然后,前面的例子就可以改成下面这样。
Functor.of(2).map(function (two) {
return two