Functor函子03-AP & IO

2.2k words

IO函子专门用于处理副作用不纯操作(例如读取文件、网络请求等)。在函数式编程中,不纯操作通常需要被特别处理,以保持函数的纯度,而IO函子提供了一种方式来对这些操作进行包装。IO函子的value是一个惰性执行的函数,它在map或其他操作被调用时才会执行。

Ap函子(Applicative Functor)

Ap(也叫应用函子)是函子的一种扩展,它实现了ap方法。这个方法允许不同的函子之间进行相互调用,从而让我们能够将一个函子的值传递给另一个函子的map方法。这种设计不仅扩展了函子的能力,还增强了它们的组合性。
>

Ap函子相较于普通的函子(如MaybeEither),增加了对多个函子的支持,使得我们能够将一个包含函数的容器应用于另一个容器的值上。

🌰 Ap 核心实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class Ap {
static of(value) {
return new this(value)
}

constructor(value) {
this.value = value
}

map(fn) {
return Ap.of(fn(this.value))
}

/** 接受函子,并将自身的 value 传入函子的map执行 */
ap(container) {
return container.map(this.value)
}
}

在上述实现中,Ap类有一个ap方法。它接收另一个函子容器作为参数,并将当前容器的value值传递给那个容子的map方法。这使得我们能够在多个函子之间实现相互应用,类似于将一个函数应用到一个容器中的值。

🌰 Ap 使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
const num1 = Ap.of(2)
const num2 = Ap.of(3)
const add = (x, y) => x + y

// [object Object][object Object] 还得拆箱
console.log(add(num1, num2))

/** 多参数还使用柯里化 */
const addOne = (x) => {
return (y) => add(x, y)
}

// 使用ap方法
console.log(num1.map(addOne).ap(num2))

在这个例子中,我们定义了两个Ap函子num1num2,并用它们进行加法操作。通过柯里化和map方法,我们将add函数应用于这些容器中的值。最后,ap方法将num1的值传递给num2map方法,计算结果并返回。

IO 函子

IO函子专门用于处理副作用不纯操作(例如读取文件、网络请求等)。在函数式编程中,不纯操作通常需要被特别处理,以保持函数的纯度,而IO函子提供了一种方式来对这些操作进行包装。IO函子的value是一个惰性执行的函数,它在map或其他操作被调用时才会执行。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class IO {
static of(value) {
return new this(value)
}

constructor(value) {
this.value = value
}

/** 差异化 */
map(fn) {
// 因为 value 是一个函数,所以需要执行后返回给传入的fn
return IO.of(fn(this.value()))
}

isNothing() {
return this.value === null || this.value === undefined
}

join() {
return this.isNothing() ? MayBe.of(null) : this.value
}

chain(fn) {
return this.map(fn).join()
}
}

IO类与其他函子类似,也有map方法,但与传统的map方法不同,IOvalue并不是一个普通的值,而是一个函数。map方法将会执行该函数并将结果传递给后续的操作。

🍐 IO 使用

1
2
3
4
5
6
7
8
const fs = require('fs')

// readFileSync 返回一个IO函子,此时读取文件未执行(惰性)
const readFileSync = (file) => new IO(() => fs.readFileSync(file))

// 获取 01.txt 文件内容
const file = readFileSync('./01.txt').chain((out) => out.toString())
console.log(file)

在这个例子中,readFileSync返回了一个惰性执行的IO函子。直到我们调用chain方法时,文件的读取操作才会被执行。这种设计避免了副作用在函数外部直接发生,同时保持了函数的可组合性。通过chain方法,我们能够在IO函子中执行一系列的操作,从而使得副作用得到了有效的控制。

IO 函子的用途

IO函子的主要用途是处理那些需要产生副作用的操作,例如文件读写、网络请求或是其他不纯的操作。在函数式编程中,这种操作往往被视为不纯的,而IO函子将它们包装成纯函数,使得程序能够以更加安全、可组合的方式处理这些副作用。

Comments