我一直在学习函数式编程,并且遇到过 Monads、Functors 和 Applicatives。
根据我的理解,以下定义适用:
a) ( A=>B ) => C[A] => C[B] |函子
b) ( A=>C[B] ) => C[A] => C[B] |单子
c) ( C[A=>B] ) => C[A] => C[B] |适用性
(参考: https://thedet.wordpress.com/2012/04/28/functors-monads-applicatives-can-be-so-simple/ )
此外,我知道 Monad 是 Functor 的特例。就像它应用一个 将包装值返回 包装值的函数并返回包装值。
当我们使用 Promise.then(func)
时,我们传递给 Promise(即 C[A])一个通常具有签名的函数 A => B
并返回另一个 Promise(即 C[B])。所以我的想法是,Promise 只能是 Functor 而不是 Monad,因为 func
返回 B 而不是 C[B]。
但是,谷歌搜索我发现 Promise 不仅是 Functor,还是 Monad。我想知道为什么 func
不返回包装值 C[B] 而只是 B。我错过了什么?
原文由 Jack Spar 发布,翻译遵循 CC BY-SA 4.0 许可协议
更新。请参阅这个新库,它为没有 theneables 问题的普通基于回调的函数提供仿函数和 monad 运算符:
https://github.com/dmitriz/cpsfy
JS Promise 既不是 Functor 也不是 Applicative 也不是 Monad
它不是函子,因为违反了 组合保存法则(将函数的组合发送到它们的图像的组合):
不等于
这在实践中意味着什么,重构永远不安全
至
本来应该是
Promise
一个函子。违反函子定律的证明。 这是一个反例:
这是 Codepen 上的示例: https ://codepen.io/dmitriz/pen/QrMawp?editors=0011
解释
Since the composition
h
is the identity function,promise.then(h)
simply adopts the state ofpromise
, which is already fulfilled with the identitya => a
.另一方面,
f
返回所谓的 thenable :为了维护仿函数法则,
.then
必须简单地将结果包装成 promisef(x)
。相反,当内部函数.then
返回“thenable”时, Promise 规范 需要不同的行为。根据 2.3.3.3 ,身份函数id = a => a
存储在then
密钥下被称为其中
resolvePromise
和rejectPromise
是承诺解析过程提供的两个回调函数。但是,为了得到解决或拒绝,必须调用这些回调函数之一,这永远不会发生!因此,最终的 promise 仍处于 pending 状态。结论
在此示例中,
promise.then(x => g(f(x)))
由身份函数a => a
实现,而promise.then(f).then(g)
永远处于挂起状态。因此这两个承诺不等价,因此违反了函子法则。Promise 既不是 Monad 也不是 Applicative
因为即使是来自 Pointed Functor Spec 的自然变换法则,也就是 Applicative 的一部分(同态法则),也被违反了:
证明。 这是一个反例:
Codepen 上的这个例子: https://codepen.io/dmitriz/pen/wjqyjY? editors=0011
结论
在此示例中,一个 promise 已实现,而另一个 promise 尚未完成,因此两者在任何意义上都不等价,这违反了法律。
更新。
“成为函子”到底是什么意思?
Promise 本身是 一个 Functor/Applicative/Monad,与通过更改其方法或添加新方法来 实现它 之间似乎存在混淆。然而,一个 Functor 必须有一个已经提供的
map
方法(不一定是这个名字),而成为一个 Functor 显然取决于这个方法的选择。方法的实际名称没有任何作用,只要满足规律即可。对于 Promises,
.then
是最自然的选择,它不符合函子定律,如下所述。据我所知,其他任何 Promise 方法都不会以任何可能的方式使它成为 Functor。更改或添加方法
至于能不能定义出 其他符合规律的方法,那就另当别论了。据我所知,在这个方向上的唯一实现是由 creed 库 提供的。
但是要 付出相当大的代价:不仅需要定义全新的
map
方法,还需要更改 promise 对象本身:acreed
promise can hold a “ theneable”作为值,而原生 JS Promise 不能。这种变化是实质性的,并且是必要的,以避免在下面解释的示例中违反法律。特别是,我不知道有什么方法可以在不进行此类基本更改的情况下将 Promise 变成 Functor(或 Monad)。