Functor函子
定义:容器:包含值和值得变形关系(这个变形关系就是函数) 函子:是一个特殊的容器,通过一个普通的对象来实现,该对象具有map方法,map方法可以运行一个函数对值进行处理(变形关系); 函子内部有一个值;对外还有一个方法map方法。
定义一个函子
class Container {
// 定义一个静态方法 static of就是方法名
static of (value) {
// 返回一个函子对象
return new Container(value)
}
constructor (value) {
// 函子内要公布一个值;这个值不对外公布
this._value = value
}
// map方法时候接收一个处理值得纯函数。返回一个新的新函子来保存。
map (fn) {
return this.isNothing() ? Container.of(null) : Container.of(fn(this._value))
}
isNothing () {
return this._value === null || this._value === undefined
}
}
// 函子就是一个拥有map方法的一个对象
// 当创建一个函子对象的时候直接类名.调用of方法就可以了
let r = Container.of(5)
.map(x => x + 1)
.map(x => x * x)
console.log(r);
函子总结:函数式编程的运算不直接操作值,而是由函子完成。 函子就是一个实现了map锲约的对象 我们可以把函子想象成一个盒子,这个盒子内部封装了一个值 想要处理盒子中的值,我们需要给盒子的map方法传递一个处理值得函数(纯函数),由这个函数来对值进行处理。 最终map方法返回一个包含新值得函子
MayBe函子
就是对外部的空值情况做处理(控制副作用在允许的范围)
class MayBe {
// 这个of方法可以减去在外部调用maybe函子的时候后还需要再次创建new 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
}
}
let r = MayBe.of('hello world')
.map(x => x.toUpperCase())
.map(x => null)
.map(x => x.split(' '))
console.log(r);
问题:MayBe函子可以避免空值错误;但是当我们多次调用map的时候;其中一个map出问题了;我们不知道问题出在哪里。
Either函子
定义:两者中的任何一个,类似于if…else…的处理。异常会让函数变得不纯,Either函子可以用来做异常处理。
class Left {
static of (value) {
return new Left(value)
}
constructor (value) {
this._value = value
}
map (fn) {
// 这种做法可以在left中嵌入一个错误消息
return this
}
}
class Right {
static of (value) {
return new Right(value)
}
constructor (value) {
this._value = value
}
map (fn) {
return Right.of(fn(this._value))
}
}
function parseJSON (str) {
// 如果发生异常不去处理的话就不是一个纯函数
try {
return Right.of(JSON.parse(str))
}
catch (e) {
return Left.of({ error: e.message })
}
}
// let r = parseJSON('{P:zd}')
// console.log(r);
let r = parseJSON('{'name':'zd'}').map(x => x.name.toUpperCase())
console.log(r);
io函子
io函子中的_value是一个函数,这里是吧函数作为值来处理;io函子可以把不纯的动作存储到_value中,延迟执行这个不纯的操作(惰性执行),包装当前的纯操作;把不纯的操作交给调用者来处理。
const fp = require('lodash/fp')
class IO {
static of (x) {
// console.log(x, '这个是x');
return new IO(function () {
return x
})
}
constructor (fn) {
this._value = fn
}
map (fn) {
return new IO(fp.flowRight(fn, this._value))
}
}
// 案例 获取当前执行node的路径
let r = IO.of(process).map(p => p.execPath)
// console.log(r);
console.log(r._value());
console.log(111111, IO.of(process));
Monad函子
解决函子嵌套的方法:Monad函子:一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad
const fs = require('fs')
const fp = require('lodash/fp')
class IO {
static of (x) {
return new IO(function () {
return x
})
}
constructor (fn) {
this._value = fn
}
map (fn) {
return new IO(fp.flowRight(fn, this._value))
}
join () {
// 传递的函数最终也会返回一个函子
return this._value()
}
flatMap (fn) {
// map(fn)之后的函子中包裹的函数最终也会返回一个函子
return this.map(fn).join()
}
}
// 实现Linux中的cat方法(读取文件并打印)
let readFile = function (fileName) {
return new IO(function () {
// 同步读取文件readFileSync
return fs.readFileSync(fileName, 'utf-8')
})
}
// 打印
let print = function (x) {
return new IO(function () {
console.log(x, '这个是readFile返回来的函子');
return x
})
}
let cat = fp.flowRight(print, readFile)
// r 是IO(IO()) 函子嵌套 第一个_value()是打印的value,第二个是readFile的value
// let r = cat('../../package-lock.json')._value()._value()
// console.log(r);
// 解决函子嵌套的方法:Monad函子:一个函子如果具有join和of两个方法并遵守一些定律就是一个Monad
// 当我们合并的函数返回的是值;就调用map;当我们要合并的函数返回的是一个函子;就调用flatmap
let r = readFile('../../package-lock.json')
// .map(x => x.toUpperCase())
.map(fp.toUpper)
.flatMap(print)
.join()
// 什么时候使用Monad:当一个函数返回一个函子的时候
Pointed函子
Pointed函子:是实现了of静态方法的函子 of 方法是为了避免使用new来创建对象,更深层的含义是of方法用来把值放到上下文Context(把值放到容器中,使用map来处理值。)
Task函子
// Task 处理异步任务
// node 里面读取文件用的是fs模块
const fs = require('fs')
const { task } = require('folktale/concurrency/task')
const { split, find } = require('lodash/fp')
// 读取文件的函数
function readFile (fileName) {
return task(resolver => {
// 异步读取文件 fs.readFile的参数有三个;第一个是文件的路径,第二个是编码格式,第三个是回调函数。在node里面是错误优先;所以回掉函数的参数是err,data
fs.readFile(fileName, 'utf-8', (err, data) => {
if (err) resolver.reject(err)
resolver.resolve(data)
})
})
}
// 调用
// let task1 = readFile('../../package-lock.json')
// console.log(task1, '返回的是一个task函子');
// task函子必须要有run()方法;run方法就是去执行
readFile('../../package-lock.json')
.map(split('\n'))
.map(find(x => x.includes('version')))
.run()
.listen({
onRejected: err => {
console.log(err);
},
onResolved: data => {
console.log(data);
}
})
|