这篇文章总结于阮一峰老师的博客
functional programming is the process of building software by composing pure functions, avoid shared state, mutable data and side-effects.
特点
1.函数是第一等公民
指的是函数与其他数据类型一样,可以赋值给其他变量,也可以作为参数传入另一个函数,或者作为别的函数的返回值
var print = function(i){ console.log(i);};
[1,2,3].forEach(print);
2. 只用expression,不用statement
expression是一个单纯的运算过程,总是有返回值,statement是执行某种操作,没有返回值
3.没有side effect
所谓side effect,指的是函数内部和外部互动,产生运算以外的其他结果。
没有side effect 意味着函数要保持独立,所有功能就是返回一个新的值,没有其他行为,尤其是不得修改外部变量的值
4. 不修改状态
函数式编程使用参数保存状态
5. 引用透明
指的是函数的运行不依赖于外部变量或者状态,只依赖于输入的参数,任何时候只要参数相同,引用函数所得到的返回值总是相同的
范畴论
In mathematics, a category is an algebraic structure that comprises "objects" that are linked by "arrows".
我们可以把范畴想象成是一个容器,里面包括:
值和值的变形关系也就是函数
函数的合成和柯里化
函数的合成
如果一个值要经过多个函数,才能变成另一个值,就可以把所有中间步骤合并成一个函数
const compose = function (f, g) {
return function (x) {
return f(g(x));
};
}
柯里化
f(x)和g(x) 合并成 f(g(x)),有一个隐藏的前提,就是f和g都只能接受一个参数
function add(x, y){
return x+y;
}
add(1, 2) //3
function addX(y){
return function (x){
return x + y;
};
}
addX(2)(1) //3
一般约定,函子的标志就是容器具有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('bomb')).map(_.concat(' away')).map(_.prop('length'));
### of方法
函数式编程一般约定,函子有一个of方法,用来生成新的容器
Functor.of = function(val){
return new Functor(val);
}
Maybe函子
函子接受各种函数,处理容器内部的值。这里就有一个问题,容器内部的值可能是一个null,而外部函数未必有处理控制的机制,如果传入空值,很可能就会出错
class Maybe extends Functor {
map(f) {
return this.val ? Maybe.of(f(this.val)) : Maybe.of(null);
}
}
Either函子
Either函子内部有两个值:左值left和右值Right.
正常情况下,使用右值,左值是右值不存在时使用的默认值
class Either extends Functor {
constructor(left, right) {
this.left = left;
this.right = right;
}
map(f) {
return this.right ?
Either.of(this.left, f(this.right)) :
Either.of(f(this.left), this.right);
}
}
Either.of = function (left, right) {
return new Either(left, right);
};
下面是用法
var addOne = function (x) {
return x + 1;
};
Either.of(5, 6).map(addOne);
// Either(5, 7);
Either.of(1, null).map(addOne);
// Either(2, null);
EIther函子的另一个用途是代替try...catch,使用左值表示错误
###ap函子
函数里面包含的值,完全可能是函数
class Ap extends Functor {
ap(F) {
return Ap.of(this.val(F.val));
}
}
因此前面例子可以写成
Ap.of(addTwo).ap(Functor.of(2))
// Ap(4)
function add(x) {
return function (y) {
return x + y;
};
}
Ap.of(add).ap(Maybe.of(2)).ap(Maybe.of(3));
// Ap(5)
### Monad函子
Monad函子的作用是,总是返回一个单层的函子,它有一个flatMap方法,与map方法作用相同,唯一的区别是如果生成了一个嵌套函子,它会取出后者内部的值,保证返回的永远是一个单层的容器,不会出现嵌套的情况。
### IO操作
Monad 函子的重要应用,就是实现 I/O (输入输出)操作。
I/O 是不纯的操作,普通的函数式编程没法做,这时就需要把 IO 操作写成Monad函子,通过它来完成。
var fs = require('fs');
var readFile = function(filename) {
return new IO(function() {
return fs.readFileSync(filename, 'utf-8');
});
};
var print = function(x) {
return new IO(function() {
console.log(x);
return x;
});
}