为什么Promises Monads?

Jac*_*par 22 javascript monads functional-programming functor es6-promise

我一直在学习函数式编程,并且遇到了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.我错过了什么?

Dmi*_*sev 29

承诺既不是Functor也不是Applicative,也不是Monad

它不是一个仿函数,因为 违反了构图保存法(将函数的组合发送到图像的组合):

promise.then(x => g(f(x))) 
Run Code Online (Sandbox Code Playgroud)

实际上,这意味着重构永远不会安全

promise.then(f).then(g)
Run Code Online (Sandbox Code Playgroud)

promise
  .then(x => f(x))
  .then(y => g(y))
Run Code Online (Sandbox Code Playgroud)

就像它本来一样,是Promise一个仿函数.

函子违法行为的证明.这是一个反例:

promise
  .then(x => g(f(x))
Run Code Online (Sandbox Code Playgroud)

以下是Codepen上的示例:https://codepen.io/dmitriz/pen/QrMawp edit = 0011

说明

由于组合h是身份功能,因此promise.then(h)只需采用promise已经用身份完成的状态a => a.

另一方面,f返回所谓的那么:

1.2."thenable"是定义then方法的对象或函数.

为了维护仿函法,.then必须简单地将结果包含在承诺中f(x).相反,当函数内部返回"thenable" 时,Promise Spec需要不同的行为.then.根据2.3.3.3,密钥id = a => a下存储的身份函数then称为

//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)
       })

其中resolvePromiserejectPromise是由promise解决程序提供的两个回调函数.但是,为了得到解决或拒绝,必须调用其中一个回调函数,这些函数永远不会发生!因此,由此产生的承诺仍处于待决状态.

结论

在此示例中, promise.then(x => g(f(x))) 使用标识函数实现a => a,而 promise.then(f).then(g) 永远保持在挂起状态.因此,这两个承诺并不等同,因此违反了仿函法.


承诺既不是Monad也不是Applicative

因为即使是Pointed Functor Spec 的自然变换定律,也就是属于Applicative(同态定律)的一部分,也被违反了:

id(resolvePromise, rejectPromise)
Run Code Online (Sandbox Code Playgroud)

证明.这是一个反例:

Promise.resolve(g(x)) is NOT equivalent to Promise.resolve(x).then(g)
Run Code Online (Sandbox Code Playgroud)

Codepen上的这个例子:https://codepen.io/dmitriz/pen/wjqyjY edit = 0011

结论

在这个例子中,一个承诺得以实现,而另一个承诺未决,因此这两个在任何意义上都不相同,违反了法律.

  • 错误的答案。当我们将“ then prop”下的内容替换为“ foo” prop下的内容时,法律就完全满足了。莫纳德(Monad)法律不以编程语言破解保留字。 (6认同)
  • .@KenOKABE 你说得对,但是 `then` 不是 JS 中的保留字,是吗?虽然我相信(由于我对 monad 和朋友的理解非常有限)OP 在严格的数学意义上是正确的,但 Promise 在实际意义上仍然像 monad 一样工作。Tomas Petricek 在他的优秀论文 [当我们谈论 monad 时谈论什么](http://tomasp.net/academic/papers/monads/monads-programming.pdf) 中讨论了同样的观点差异。虽然某些东西在正式(数学)层面上不是 monad,但从实现的角度来看,它仍然可以有效地成为一个 monad。 (5认同)

小智 7

Promise(很像)一个 monad,因为then它过载了。

当我们使用 Promise.then(func) 时,我们向 Promise(即 C[A])传递一个通常具有签名 A => B 的函数并返回另一个 Promise(即 C[B])。所以我的想法是 Promise 只是一个 Functor 而不是 Monad,因为 func 返回 B 而不是 C[B]。

这是真的then(Promise<A>, Func<A, B>) : Promise<B>(如果你原谅我的 javascript 类型的伪代码,我会将函数描述为好像this是第一个参数)

无极API用品另一个签名then虽然,then(Promise<A>, Func<A, Promise<B>>) : Promise<B>。这个版本显然符合 monadic bind ( >>=)的签名。自己试试看,确实有效。

然而,为 monad 拟合签名并不意味着 Promise一个 monad。它还需要满足单子的代数定律。

monad 必须满足的定律是结合律

(m >>= f) >>= g ? m >>= ( \x -> (f x >>= g) )
Run Code Online (Sandbox Code Playgroud)

以及左右身份的法则

(return v) >>= f ? f v
m >>= return ? m
Run Code Online (Sandbox Code Playgroud)

在 JavaScript 中:

(m >>= f) >>= g ? m >>= ( \x -> (f x >>= g) )
Run Code Online (Sandbox Code Playgroud)

我想任何熟悉 Promise 的人都可以看到所有这些都应该是真实的,但您可以随意尝试一下。

因为 Promise 是一个 monad,我们也可以从中派生ap并得到一个应用程序,给我们一些非常好的语法,但有点不明智:

(return v) >>= f ? f v
m >>= return ? m
Run Code Online (Sandbox Code Playgroud)

  • `Promise` 不是一个 monad,请参阅我的答案 /sf/answers/3512139081/ (2认同)

小智 5

Promise 不是包含 then 属性的对象上的 Monad

Promise 将包含 then 属性(即函数)的对象视为特殊情况。因此,他们违反了左同一律,如下所示:

//Law of left identity is violated
// g(v) vs Promise.resolve(v).then(g)

// identity function saved under `then` prop
const v = ({then: x=>x({then: 1})})

// `g` returns the `then` prop from object wrapped in a promise
const g = (obj => Promise.resolve(obj.then))

g(v).then(res =>
          console.log("g(v) returns", res))
// "g(v) returns" x => x({ then: 1 })


Promise.resolve(v).then(g)
  .then(res =>
        console.log("Promise.resolve(v).then(g) returns", res))
// "Promise.resolve(v).then(g) returns" 1
Run Code Online (Sandbox Code Playgroud)

Codepen 上的示例

发生这种情况是因为resolve将then属性下的函数视为回调,将then链的延续作为参数传递,而不是创建包含它的promise。这样,它就不像单元一样发挥作用,并导致违反单子定律。

但是,对于不包含 then 属性的值,它应该充当 monad。


Red*_*edu -5

根据我的说法,Promise 是函子、应用函子和单子,因为它们遵守函子和单子定律。

好吧,让我们研究一下函子案例。为了使 Promise 成为 Functor 的实例,我们必须为 Promise定义一个fmap函数并通过 Functor 法则。什么是函子定律?(a -> b) - f a -> f bfmap

fmap id      = id
fmap (p . q) = (fmap p) . (fmap q)
Run Code Online (Sandbox Code Playgroud)
  • id是恒等函数。我们可以像var一样简单地在JS中实现它id = x => x
  • in是复合运算,就像数学中一样.(p . q)它本质上是var dot = p => q => x => p(q(x))在 JS 中。

JS 中的问题在于,对象(包括函数)都是引用类型,这意味着与 Haskell 不同,每次部分应用函数时,都会得到一个不同的函数做同样的事情。因此,仅以下法律中的权益检查将会失败,但如果您检查结果值,它们就会通过。

fmap id      = id
fmap (p . q) = (fmap p) . (fmap q)
Run Code Online (Sandbox Code Playgroud)

所以,是的,Promise 是 Functor,如果你检查Monad 法则,你可以很容易地看出它们也是 Monad。

  • `then` 的类型错误(有点像 `Promise a ~&gt; ? -&gt; Promise b`),它超载,它递归地扁平化,它同化 then-ables,它自动提升函数。仅提供一些琐碎的例子并不能提供任何证据。 (4认同)
  • `return a &gt;&gt;= f ≡ fa` 当 `a` 本身是一个 Promise 时,这条规则不适用,因为 Promise 不允许返回嵌套的 Promise。 (2认同)