日期。
查看这个新库
提供函子和单子运算符
对于普通的基于回调的函数
没有与theneables有关的问题:
https://github.com/dmitriz/cpsfy
JS Promise 既不是 Functor 也不是 Applicative 也不是 Monad
它不是函子,因为
composition preservation law
(将功能组合发送到其图像组合)
被违反:
promise.then(x => g(f(x)))
不等于
promise.then(f).then(g)
这实际上意味着什么,
重构永远不安全
promise
.then(x => f(x))
.then(y => g(y))
到
promise
.then(x => g(f(x))
Promise 本来就是一个函子。
违反函子定律的证明。这是一个反例:
//函子组成保存律:
// promise.then(f).then(g) 与 promise.then(x => g(f(x)))
// f 接受函数 `x`
// 并将其保存在 `then` 属性下的对象中:
const f = x => ({then: x})
// g 从对象返回 `then` 属性
const g = obj => obj.then
// h = compose(g, f) 是恒等式
常量 h = x => g(f(x))
// 使用身份函数履行承诺
常量承诺 = Promise.resolve(a => a)
// 这个promise是通过identity函数实现的
promise.then(h)
.then(res => {
console.log("then(h) 返回:", res)
})
// => "then(h) 返回:" a => a
// 但是这个承诺永远不会实现
承诺.then(f)
.then(g)
.then(res => {
console.log("then(f).then(g) 返回:", res)
})
// => ???
// 因为这个不是:
承诺.then(f)
.then(res => {
console.log("then(f) 返回:", res)
})
这是 Codepen 上的示例:
https://codepen.io/dmitriz/pen/QrMawp?editors=0011
说明
由于组合h是恒等函数,所以promise.then(h)简单地采用promise的状态,它已经用恒等a => a实现了。
另一方面,f 返回所谓的thenable:
1.2。 “thenable”是定义 then 方法的对象或函数。
为了维护函子定律,.then 必须简单地将结果 f(x) 包装成 promise。相反,当.then 中的函数返回“thenable”时,Promise Spec 需要不同的行为。根据2.3.3.3,存储在then键下的身份函数id = a => a被称为
id(resolvePromise, rejectPromise)
其中resolvePromise 和rejectPromise 是promise 解析过程提供的两个回调函数。但是,为了解决或拒绝,必须调用这些回调函数之一,这永远不会发生!因此,生成的 promise 仍处于待处理状态。
结论
在这个例子中,
promise.then(x => g(f(x)))
用身份函数a => a 实现,
然而
promise.then(f).then(g)
永远处于待处理状态。
因此这两个承诺不等价
因此违反了函子定律。
因为即使是 Pointed Functor 规范中的自然变换法则,也就是 Applicative(同态法则)的一部分,也被违反了:
Promise.resolve(g(x)) is NOT equivalent to Promise.resolve(x).then(g)
证明。这是一个反例:
// 标识函数保存在 `then` 属性下
const v = ({then: a => a})
// `g` 从对象返回 `then` 属性
const g = obj => obj.then
// `g(v)` 是恒等函数
Promise.resolve(g(v)).then(res => {
console.log("resolve(g(v)) 返回:", res)
})
// => "resolve(g(v)) 返回:" a => a
// `v` 被解包成永远挂起的 Promise
// 因为它从不调用任何回调
Promise.resolve(v).then(g).then(res => {
console.log("resolve(v).then(g) 返回:", res)
})
// => ???
Codepen 上的这个例子:https://codepen.io/dmitriz/pen/wjqyjY?editors=0011
结论
在这个例子中,一个promise再次实现,而另一个promise正在等待,因此两者在任何意义上都不等同,违反了法律。
更新。
“成为 Functor”到底是什么意思?
Promise 成为 Functor/Applicative/Monad 与通过更改其方法或添加新方法使其成为 之间似乎存在混淆.然而,一个 Functor 必须已经提供了一个 map 方法(不一定在这个名称下),并且作为一个 Functor 显然取决于这个方法的选择。方法的实际名称没有任何作用,只要符合规律即可。
对于 Promises,.then 是最自然的选择,它不符合函子定律,如下所述。据我所知,其他任何 Promise 方法都不会以任何可以想象的方式使其成为 Functor。
更改或添加方法
其他方法是否可以定义符合法律规定则另当别论。我知道在这个方向上的唯一实现是由creed library 提供的。
但是要付出相当大的代价:不仅需要定义全新的map 方法,而且还需要更改promise 对象本身:creed promise 可以持有一个“theneable”作为值,而原生 JS Promise 不能。这一变化是实质性的,也是必要的,以避免违反示例中的法律,如下所述。特别是,我不知道有任何方法可以在没有这些根本性改变的情况下将 Promise 变成 Functor(或 Monad)。