一 函子 Functor
函子就是个容器,他有个值并且有个map方法,map方法可以运行一个函数对值进行处理;如何把副作用控制在可控的范围内、异常操作、异步操作等可能需要用到函子;
// 函子
// class Container {
// constructor(value) {
// this._value = value;
// }
// map(fn) {
// return new Container(fn(this._value));
// }
// }
// const a = new Container(5)
// .map( x => x + 1)
// .map( x => x * x);
// console.log(a);
class Container {
static of (value) {
return new Container(value)
}
constructor(value) {
this._value = value;
}
map(fn) {
return new Container(fn(this._value));
}
}
console.log(Container.of(5).map(x => x + 1).map( x => x * x))
函数式编程的运算不直接操作值,而是由函子来完成;函子就是一个实现了map契约的对象;我们可以把函子想象成一个盒子,这个盒子里封装了一个值;想要处理函子中的值,我们需要给盒子的map方法传递一个处理值的函数(纯函数),由这个函数对值进行处理;最终map方法返回一个包含新值的盒子;
二 mayBe函子
处理异常操作
class Maybe {
static of (value) {
return new Maybe(value)
}
constructor(value) {
this._value = value;
}
map(fn) {
return this.isNothing() ? Maybe.of(null): Maybe.of(fn(this._value))
}
isNothing() {
return this._value === null || this._value === undefined;
}
}
// console.log(Maybe.of('hello world').map(x => x.toUpperCase()));
// 可以处理空值
// const r = Maybe.of(null).map(x => x.toUpperCase());
// 发生在哪一步不知道
const r = Maybe.of(null)
.map(x => x.toUpperCase())
.map(x => null)
.map(x => x.split(' '));
console.log(r)
三 either函子
两者中的一个,类似于if…else…的处理,异常会让函数变的不纯,either函子可以用来做异常处理;
class Container {
static of (value) {
return new Container(value)
}
constructor(value) {
this._value = value;
}
map(fn) {
return Container.of(fn(this._value));
}
}
class Left extends Container{
map(fn) {
return this
}
}
class Right extends Container{
}
function parseJSON (str) {
try{
return Right.of(JSON.parse(str))
} catch(error) {
return Left.of({error: error.message})
}
}
// const r1 = Right.of(12)
// .map(x => x + 2);
// const r2 = Left.of(12)
// .map(x => x + 2);
// const r3 = parseJSON('{name: zhangSan}')
const r4 = parseJSON('{"name": "zhangSan"}')
.map(x => x.name.toUpperCase())
// console.log(r1)
// console.log(r2)
// console.log(r3)
console.log(r4)
四 IO函子
IO函子中的_value是个函数,这里把函数当作值处理;IO函子可以把不纯的动作存储到_value中,延迟执行这个不纯的操作(惰性行为),包装当前的操作;把不纯的操作当作调用者来处理;
const fp = require('loadsh/fp');
class IO {
static of(value) {
return new IO(function() {
return value;
});
}
constructor(fn) {
this._value = fn;
}
map (fn) {
return new IO(fp.flowRight(fn, this._value))
}
}
// 调用
let r = IO.of(process).map( p => p.execPath);
// console.log(r);
console.log(r._value());
五 folktale
一个标准的函数式编程库,异于lodash、ramda,他没有提供很多功能函数;只提供了函数式的操作,如compose、curry等,一些函子Task、Eithger、Maybe等;
异步任务的实现过于复杂,用folktale中的task来演示
// folktale 中的 curry/compose函数
const {compose, curry} = require ('folktale/core/lambda');
const {toUpper, first} = require('lodash/fp');
// let f = curry(2, (x, y) => x + y);
// console.log(f(2,3));
// console.log(f(2)(3))
const f = compose(toUpper, first);
console.log(f(['one', 'two']))
folktale task异步执行 folktale2.和folktal1.中的task区别很大,1.0中的用法更接近我们现在演示的函子
const fs = require('fs');
const {task} = require('folktale/concurrency/task');
const {split, find} = require('loadsh/fp');
function readFile(fileName) {
return task(resolver => {
fs.readFile(fileName, 'utf-8', (err, data) => {
if(err) resolver.reject(err)
resolver.resolve(data)
})
})
}
readFile('package.json')
.map(split('\n'))
.map(find(x => x.includes('version')))
.run()
.listen({
onRejected: err => {
console.log(err)
},
onResolved: data => {
console.log(data)
}
})
六 pointed函子
实现了of静态方法的函子,of方法是为了避免使用new来创建对象,更深层次的含义是of方法用来把值放到上下文context(把值放到容器中,使用map来处理值)
七 io函子的问题
const fp = require('loadsh/fp');
const fs = require('fs');
class IO {
static of(value) {
return new IO(function() {
return value;
});
}
constructor(fn) {
this._value = fn;
}
map (fn) {
return new IO(fp.flowRight(fn, this._value))
}
}
const readFile = function(fileName) {
return new IO(function(){
return fs.readFileSync(fileName, 'utf-8')
});
}
const print = function(x) {
return new IO(function(){
console.log(x);
return x;
})
}
const cat = fp.flowRight(print, readFile);
const r = cat('package.json')._value()._value();
console.log(r)
八 monad 函子
可以变扁的poinged函子。变扁就是解决函数嵌套的问题;一个函子如果具有join和of的两个方法并遵守一些定律就是monad;
const fp = require('loadsh/fp');
const fs = require('fs');
class IO {
static of(value) {
return new IO(function() {
return value;
});
}
constructor(fn) {
this._value = fn;
}
map (fn) {
return new IO(fp.flowRight(fn, this._value))
}
join() {
return this._value()
}
flatMap(fn) {
return this.map(fn).join();
}
}
const readFile = function(fileName) {
return new IO(function(){
return fs.readFileSync(fileName, 'utf-8')
});
}
const print = function(x) {
return new IO(function(){
console.log(x);
return x;
})
}
const r = readFile('package.json')
.map(fp.toUpper)
.flatMap(print)
.join()
console.log(r)
九 函子的练习
support.js
class Container {
static of(value) {
return new Container(value);
}
constructor(value) {
this._value = value;
}
map(fn) {
return Container.of(fn(this._value));
}
}
class MayBe {
static of(x) {
return new MayBe(x);
}
constructor(x) {
this._value = x;
}
isNothing() {
return this._value === null || this._value === undefined;
}
map(fn) {
return this.isNothing() ? this : MayBe.of(fn(this._value));
}
}
module.exports = { MayBe, Container };
const fp = require('loadsh/fp');
const {MayBe, Container} = require('./support');
// 使用fp.add 和 fp.map创建一个能让functor里的值增加的函数
let mayBe = MayBe.of([5, 6, 1]);
let ex1 = () => {
return mayBe.map(fp.map( item => fp.add(item, item)))
}
console.log(ex1())
// 使用一个函数ex2 能够使用fp.first获取列表的第一个元素
let xs = Container.of(['do','ray','me','fa','so','la','ti','do']);
let ex2 = () => {
return xs.map(fp.first)
}
console.log(ex2())
// 实现一个函数ex3, 使用 safeProp 和 fp.first 找到user 名字的首字母
let safeProp = fp.curry(function(x, o) {
return MayBe.of(o[x])
});
let user = {id: 2, name: 'Albert'};
let ex3 = () => {
return safeProp('name', user).map(fp.first);
}
console.log(ex3())
// 使用 Maybe 重写ex4,不要有if语句
// let ex4 = function(n) {
// if(n) return parseInt(n);
// }
let ex4 = (n) => {
return MayBe.of(n).map(parseInt)
}