函数式编程的 Monad
Monad 是函数式编程中的一种概念,它为数据提供了一种结构化的处理方式。
简单来说就是封装一组方法给传入数据使用,但与对象不同,Monad 是不可变的,每次进行修改都会返回一个新的 Monad。
在函数式编程中,我们通常需要避免副作用。但在一些常见的,例如网络请求,输入输出和文件读写等场景,网络会有波动,输入输出不可控,文件内容可能会变,这种情况下,副作用是不可避免的。
通过 Monad,可以将这些副作用封装在一处。使得副作用可控,乃至于在纯函数中使用。
创建 Monad 的三个条件
如果想创建一个 Monad,那么首先需要满足以下三个条件:
包装数据:Unit(构造函数)
提供一个函数,用于将数据包装成一个对象,并赋予方法。
const unit = v => ({
get: ()=> v,
set: v => unit(v)
})
绑定方法:Bind(绑定操作)
允许提供自定义方法来处理封装的对象,并返回新的 Monad。
const unit = v => ({
get: ()=> v,
set: v => unit(v),
bind: fn => unit(fn(v)),
flatBind: fn => fn(v)
})
const a = unit(1)
const b = a.bind(v => v * 2)
a.get() // 1
b.get() // 2
遵循三定律
为了确保 Monad 的行为符合预期,它需要遵循以下三个定律:
左单位律:unit(x).bind(f) === f(x)
包装的数据通过函数 f 处理的结果,永远等于直接通过函数 f 处理包装的数据。
const unit = v => ({ get: ()=> v, set: v => unit(v), bind: fn => unit(fn(v)), flatBind: fn => fn(v) }) const f = x => x * 2 const r1 = unit(5).bind(f) const r2 = f(5) console.log(r1.get() === r2) // true
右单位律:m.bind(unit) === m
包装的数据重新包装后,永远等于包装的数据。
const unit = v => ({ get: ()=> v, set: v => unit(v), bind: fn => unit(fn(v)), flatBind: fn => fn(v) }) const r1 = unit(5) const r2 = r1.flatBind(unit) console.log(r1.get() === r2.get()) // true
结合律:(m.bind(f)).bind(g) === m.bind(x => f(x).bind(g))
包装的数据先通过函数 f 处理,再通过函数 g 处理,永远等于在函数 f 中处理包装数据后再通过函数 g 处理。
const unit = v => ({ get: ()=> v, set: v => unit(v), bind: fn => unit(fn(v)), flatBind: fn => fn(v) }) const f = x => unit(x * 5) const g = x => unit(x / 2) const r1 = unit(5).bind(f).bind(g) const r2 = unit(5).bind(v => g(f(v))) console.log(r1.get() === r2.get()) // true
函数式编程的 Monad
http://www.inksha.com/archives/han-shu-shi-bian-cheng-de-monad