函子用于控制副作用的范围。
容器:包含值及值的变形关系(函数)
函子:特殊的容器,包含一个map方法,map用于处理内部的关系
函数式编程的运算不直接操作值,而是由函子来完成
函子是一个实现了map的契约对象
想要处理函子里的值,需要给map方法传递一个处理函数(纯函数)进行值的处理
map返回一个包含新值的函子
1.MayBe函子
用于解决外部传入值为空值的情况(控制副作用在允许的范围内)
// 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
}
}
2.Either函子
两者中的一个,类似if...else...(用于处理异常情况)
用于处理异常,打印错误信息
包含2个函子Left和Right。一个处理正常信息,一个处理异常
3.IO函子
IO函子中的_value是一个函数,这里把函数作为值来处理
IO函子 把不纯的函数操作存储到_value里,来保持当前操作为纯。
4.Task异步执行
folktale(一个标准的函数式编程库)中的Task函子进行异步任务执行
安装folktale : npm i folktale
导入Task函子: const { task } = require('folktale/concurrency/task'}
task(resolver =>{}) 返回一个Task对象,类似于Promise
Task和Promise的不同
- 方法混淆
Task 的 map / chain / fork 在 Promise 中全都是 then 方法。这样的 API 设计让 Promise 更好用,但也失去了一些函数式的特性,尤其是 fork 和另两个方法的意义是完全不同的
- 立即执行 vs 延迟执行
Task 的异步流直到 fork 之前都仅仅是「动作」,没有「执行」,而 Promise 在生成的当下即发起了异步流程,这个的不同造成了这两种数据流程的根本不同。
- 多次订阅 vs 单次调用
因为上面执行时机的不同,Task 可以分化出很多不同的异步流程,每个流程都可以被多次 fork 执行,而 Promise 流程只会执行一次。
- 异步微任务 vs 纯粹的回调
即使是用 Promise 直接 resolve 一个结果,仍会生成一个异步微任务,排在在同步流程之后执行。这让 Promise 的数据流不适合兼容同步的数据流程。
而 Task 由于仅仅是纯粹一系列的函数回调组合,它只会根据需要产生异步流程,因而能够很好地兼容同步流程。
IO 函子的所有支持的同步事务,用 Task 可以等价兼容。这使得「一种结构解决所有问题」的函数式目标成为可能。
- 更灵活精确的流程控制
通过对 Task 的改良,可以实现请求缓存,截流,防抖等多种细致的流程控制,实现对复杂逻辑的精细拆分。
Point函子
实现了of静态方法的函子
of方法的目的是为了避免new一个新的对象,将值放到上下文中,然后通过map对值进行处理。
IO函子存在的问题
IO(IO(X))
函子嵌套时需要读取其中的值特别不方便。
Monad函子
是可以变扁的Pointed函子(需要具有join和of两个方法,并遵循某些规律)
Monad可以处理函数嵌套的问题
利用.join()获取函子中的value