更新日期。
查看这个新库
提供函子和单子运算符
对于简单的基于回调的函数
不存在以下问题:
https://github.com/dmitriz/cpsfy
JS Promise 既不是 Functor,也不是 Applicative,也不是 Monad
它不是一个函子,因为成分保存法则(将函数组合发送到其图像组合)
被侵犯:
promise.then(x => g(f(x)))
不等于
promise.then(f).then(g)
这在实践中意味着什么,
重构永远不安全
promise
.then(x => f(x))
.then(y => g(y))
to
promise
.then(x => g(f(x))
本来会是这样Promise
一个函子。
违反函子定律的证明。这是一个反例:
//Functor composition preservation law:
// promise.then(f).then(g) vs promise.then(x => g(f(x)))
// f takes function `x`
// and saves it in object under `then` prop:
const f = x => ({then: x})
// g returns the `then` prop from object
const g = obj => obj.then
// h = compose(g, f) is the identity
const h = x => g(f(x))
// fulfill promise with the identity function
const promise = Promise.resolve(a => a)
// this promise is fulfilled with the identity function
promise.then(h)
.then(res => {
console.log("then(h) returns: ", res)
})
// => "then(h) returns: " a => a
// but this promise is never fulfilled
promise.then(f)
.then(g)
.then(res => {
console.log("then(f).then(g) returns: ", res)
})
// => ???
// because this one isn't:
promise.then(f)
.then(res => {
console.log("then(f) returns: ", res)
})
这是 Codepen 上的示例:https://codepen.io/dmitriz/pen/QrMawp?editors=0011
解释
自从组成h
是恒等函数,promise.then(h)
简单地采用以下状态promise
,已经满足恒等式a => a
.
另一方面,f
返回所谓的thenable:
1.2. “thenable”是定义 then 方法的对象或函数。
为了维护函子定律,.then
必须简单地将结果包装到 Promise 中f(x)
。相反,承诺规格当函数内部需要不同的行为.then
返回一个“thenable”。按照2.3.3.3,恒等函数id = a => a
存储在then
密钥被称为
id(resolvePromise, rejectPromise)
where resolvePromise
and rejectPromise
是 Promise 解析过程提供的两个回调函数。但是,为了解决或拒绝,必须调用这些回调函数之一,但这种情况永远不会发生!因此,最终的承诺仍处于待处理状态。
结论
在这个例子中,promise.then(x => g(f(x)))
满足恒等函数a => a
,
然而promise.then(f).then(g)
永远处于待处理状态。
因此这两个承诺并不等同
因此违反了函子定律。
承诺既不是Monad nor an 适用性
因为即使是自然变换定律尖函子规格,这是存在的一部分适用性(同态定律),被违反:
Promise.resolve(g(x)) is NOT equivalent to Promise.resolve(x).then(g)
Proof.这是一个反例:
// identity function saved under `then` prop
const v = ({then: a => a})
// `g` returns `then` prop from object
const g = obj => obj.then
// `g(v)` is the identity function
Promise.resolve(g(v)).then(res => {
console.log("resolve(g(v)) returns: ", res)
})
// => "resolve(g(v)) returns: " a => a
// `v` is unwrapped into promise that remains pending forever
// as it never calls any of the callbacks
Promise.resolve(v).then(g).then(res => {
console.log("resolve(v).then(g) returns: ", res)
})
// => ???
Codepen 上的这个例子:https://codepen.io/dmitriz/pen/wjqyjY?editors=0011
结论
在这个例子中,一个承诺已经履行,而另一个承诺尚未履行,因此两者在任何意义上都不等同,违反了法律。
UPDATE.
“成为 Functor”到底是什么意思?
Promise 之间似乎存在混淆beingFunctor/Applicative/Monad 本身,以及方法使它成为这样通过改变其方法或添加新方法。然而,函子必须有一个map
已经提供了方法(不一定是这个名称),并且作为一个 Functor 显然取决于这个方法的选择。方法的实际名称并不起任何作用,只要满足规律即可。
为了承诺,.then
是最自然的选择,但不符合函子定律,如下所述。据我所知,其他 Promise 方法都不会以任何可以想象的方式使其成为 Functor。
更改或添加方法
是否是另一回事其他方法可以定义为符合法律规定。据我所知,在这个方向上的唯一实现是由信条图书馆.
但有一个付出相当大的代价: 不仅是全新的map
需要定义方法,而且还需要更改 Promise 对象本身:creed
Promise 可以保存“theneable”作为值,而原生 JS Promise 则不能。这一改变是重大且必要的,以避免违反示例中的法律,如下所述。特别是,我不知道有什么方法可以在不进行此类根本性改变的情况下将 Promise 变成 Functor(或 Monad)。