函子(Functor)

182 阅读3分钟

函子用于控制副作用的范围。

容器:包含值及值的变形关系(函数)

函子:特殊的容器,包含一个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的不同

  • 方法混淆

Taskmap / chain / forkPromise 中全都是 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