为什么 Promise 是 Monad?

2023-12-15

我一直在学习函数式编程,并且接触过 Monad、Functor 和 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 而不是 Monadfunc返回 B 而不是 C[B]。

然而,谷歌搜索我发现 Promise 不仅是 Functor,而且还是 Monad。我想知道为什么,作为func不返回包装值 C[B] 而只返回 B。我缺少什么?


更新日期。 查看这个新库 提供函子和单子运算符 对于简单的基于回调的函数 不存在以下问题:

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 对象本身:creedPromise 可以保存“theneable”作为值,而原生 JS Promise 则不能。这一改变是重大且必要的,以避免违反示例中的法律,如下所述。特别是,我不知道有什么方法可以在不进行此类根本性改变的情况下将 Promise 变成 Functor(或 Monad)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

为什么 Promise 是 Monad? 的相关文章

  • HTML/VBA Click 事件未触发

    这是我第一次在 StackOverflow 上发布问题 到目前为止 我已经能够通过 VBA 帮助论坛解决我的大部分问题 我的问题很简单 我有一个自动数据拉取 我需要在其中导出数据 我过去曾在这方面取得过成功 但这次略有不同 我尝试单击以生成
  • VBA / HTML / jQuery 选择自动完成 - 在列表中选择

    我正在尝试使用 Excel 中的 VBA 在网站的列表中选择一个值 这不是一个 正常列表 该网站使用 jQuery 选择自动完成 如下所示 example http davidwalsh name demo jquery chosen ph
  • Jquery从下拉列表中获取所选值的id

    我有一个下拉列表 可以从数据库获取值 如下所示 get getJobs function jobs seljobs jobs var i 0 jobs forEach function n alert job id n id 32 67 4
  • 使用 moment.js 检查输入日期是否为星期一

    好吧 我想检查日期是否是星期一 例如 var myDate new Date moment myDate DD MM YYYY dayIs monday 在我的国家 一周的第一天是星期一 所以 我真的想检查输入日期是否是一周的开始 我尝试使
  • jQuery 选择 # id 以单词为前缀,计数器为后缀

    有没有办法用 jQuery 选择所有带有前缀 my 和后缀 0 9 的 id 像这样的 my 1 4 还是可以用循环来实现 div div div div div div div div div div 第一个想法 似乎效果很好 div i
  • 如何使用canvas.toDataURL()将画布保存为图像?

    我目前正在构建一个 HTML5 Web 应用程序 Phonegap 本机应用程序 我似乎不知道如何将画布保存为图像canvas toDataURL 有人可以帮我吗 这是代码 有什么问题吗 我的画布被命名为 canvasSignature J
  • Leaflet js虚构地图

    我是 Leaflet 的新手 我想了解如何创建完全交互式的虚构地图 我有一张图像想要转换为传单地图 该图像基本上像图表一样具有许多连接和点 我想首先将该图像转换为地图 能够将鼠标悬停在这些点上 突出显示它们并显示有关它们的信息 并且还可以在
  • 即使我可以监视其他方法,也无法监视事件处理程序

    我想使用 Jest Jasmine Enzyme 测试 React 中的事件处理程序 MyComponent js import React from react class MyComponent extends React Compon
  • 检查 touchend 是否在拖动后出现

    我有一些代码可以更改表的类 在手机上 有时表格对于屏幕来说太宽 用户将拖动 滚动来查看内容 但是 当他们触摸并拖动表格时 每次拖动都会触发 touchend 如何测试触摸端是否是触摸拖动的结果 我尝试跟踪dragstart和dragend
  • Node.js - console.log 不显示数组中的项目,而是显示 [Object]

    我在注销对象内数组的内容时遇到问题 实际的物体看起来像这样 var stuff accepted item1 item2 rejected response Foo envelope from The sender to new item1
  • JavaScript 中数组的 HTML 数据列表值

    我有一个简单的程序 它必须从服务器上的文本文件中获取值 然后将数据列表填充为输入文本字段中的选择 为此 我想要采取的第一步是我想知道如何动态地将 JavaScript 数组用作数据列表选项 我的代码是
  • Google Chrome 106 可拖动导致元素消失

    使用拖放元素时 绝对定位元素中包含的大多数其他元素都会从屏幕上消失 如果我调整窗口大小 这些元素会出现 但在开始拖动时会再次消失 我在最新版本的 Google Chrome 106 和 Beta 版本 107 0 5304 18 以及现在的
  • 是否有任何非轮询方式来检测 DOM 元素的大小或位置何时发生变化?

    很长一段时间以来 我一直在寻找一种方法来检测 DOM 元素的大小或位置何时发生变化 这可能是因为窗口调整了大小 或者因为向该元素添加了新的子元素 或者因为在该元素周围添加了新元素 或者因为 CSS 规则已更改 或者因为用户更改了浏览器的字体
  • 如何在 Angular 中从父组件访问子组件?

    I have mat paginator在子组件a中 如下所示 子组件 html
  • JS用正则表达式替换数字

    我有元素的标识符 如下所示 form book 1 2 3 我想要的是用其他值替换该标识符中的第二个数字 我将函数 match 与以下正则表达式一起使用 var regexp d d d 但它返回我包含的数组 1 2 3 2 因此 当我尝试
  • Chartjs刻度标签位置

    尝试让 Y 轴刻度标签看起来像image https i stack imgur com XgoxX png 位于秤顶部且不旋转 缩放选项当前如下所示 scales yAxes id temp scaleLabel display true
  • JavaScript 代码在不使用 ActiveX 的情况下截取网站屏幕截图

    我有一个用户与之交互的 JavaScript 应用程序 我需要保存当前界面的外观 裁剪出我需要的部分 或者通过指定div只拍摄我需要的部分 然后发送回服务器 显然任何外部服务都无法做到这一点 我需要一个 JavaScript 或Flash
  • 数据表日期范围过滤器

    如何添加日期范围过滤器 like From To 我开始进行常规搜索和分页等工作 但我不知道如何制作日期范围过滤器 我正在使用数据表 1 10 11 版本 My code var oTable function callFilesTable
  • 如何在 Google 地图 V3 中创建编号地图标记?

    我正在制作一张上面有多个标记的地图 这些标记使用自定义图标 但我还想在顶部添加数字 我已经了解了如何使用旧版本的 API 来实现这一点 我怎样才能在V3中做到这一点 注意 当您将鼠标悬停在标记上时 标题 属性会创建一个工具提示 但我希望即使
  • 用于 C# XNA 的 Javascript(或类似)游戏脚本

    最近我准备用 XNA C 开发另一个游戏 上次我在 XNA C 中开发游戏时 遇到了必须向游戏中添加地图和可自定义数据的问题 每次我想添加新内容或更改游戏角色的某些值或其他内容时 我都必须重建整个游戏或其他内容 这可能需要相当长的时间 有没

随机推荐

  • 使用多个键从 dict 中获取元素

    如果我有 d one 1 two 2 three 3 four 4 如何在一个命令中获取 一 和 三 的值 像这样的东西 out d one three But it gives an error Using 列表理解 gt gt gt d
  • JasperReports 动态输入控件

    我目前正在尝试创建具有一些特殊输入控制行为的 JasperReport 报告 我的需求是 四个输入控件 其中两个是日期框 另一个是单选按钮 根据单选按钮的选择 其中一个数据框应该可见或不可见 因此 如果选择选项 1 则显示两个日期框 如果选
  • 从 PHP 运行 Linux 命令

    我的情况有点特殊 我正在尝试从名为 Diascope 的 PHP 脚本运行视频编码程序 该程序依赖于 ImageMagick 提供的 转换 命令 我有一个 bash 脚本 它执行一个非常简单的转换 然后运行名为 Diascope 的应用程序
  • 在MapReduce Job配置中设置参数

    有没有办法在作业配置中设置参数Mapper并且可以从Reducer 我尝试了下面的代码 在映射器中 map context getConfiguration set Sum 100 在减速机中 reduce context getConfi
  • 如何将汇编代码片段转换为二进制机器代码片段?

    是否可以自动将 x86 汇编代码片段 非完整程序 转换为相应的二进制机器代码片段 例如 xor eax eax mov ebx 12 eax into 31 c0 89 43 0c 我真的只需要相应的字节集 通常我必须查看一些指令集参考并手
  • 从 STL 文件渲染 2D 图像

    我想加载 STL 文件并生成一组不同旋转的 2D 图像 我了解了使用 numpy stl 的基础知识this例如 最终得到这个代码 from stl import mesh from mpl toolkits import mplot3d
  • 如何在保持透明度的情况下旋转GD图像库中的图像?

    我正在为我的网站制作一个皮肤预览器 我需要旋转图像的某些部分来创建该图像的表示形式以供用户查看 皮肤是一个PNG文件 它的所有部分可能都是透明的 甚至根本没有 我需要能够旋转该图像 同时保持图像内部的任何透明度透明 同时还使扩展边框 您知道
  • 当我单击表单中的按钮时提交表单。如何避免这种情况?

    我使用 twitter bootstrap 并且我想使用这些单选按钮以我的形式 问题是当我单击这些按钮中的任何一个时 表单都会立即提交 如何避免这种情况 我只想使用默认按钮 例如单选按钮 from div class control gro
  • 检查返回代码(或其他代码)以确保 MSI 已正确安装

    我正在使用 NSIS 安装一些 MSI 我在用着ExecWait msiexec passive liare SETUP LOG FILE i TEMP MyMsi msi 当 MSI 与已安装的应用程序版本相同时 安装会失败 已安装此产品
  • 删除 Django Rest Framework 响应中的标头

    我正在尝试删除Server来自 django Rest Framework 响应的标头 但我没有找到简单的方法 所以我创建了一个中间件来删除它 这是我的第一次尝试 中间件 py class RemoveHeaders object def
  • Vue.js 3 - 尝试构建一个具有 2 种布局的系统

    我是 vue js 初学者 3 我尝试构建一个具有两种布局的系统 1 对于已连接的用户 1 表示未连接的用户 在我的 router index js 中 我为每个路由添加一个元 const routes path name Home met
  • webview_flutter:未捕获(承诺中)NotAllowedError:写入权限被拒绝

    预期行为 使用复制文本Copy to clipboard从网站 行为 它会抛出以下错误 并且文本不会复制到 android 剪贴板 包 webview flutter I chromium 12065 INFO CONSOLE 0 Unca
  • Excel VBA - 退出for循环

    我想退出我的for当满足内部条件时循环 我怎样才能退出我的for循环时if条件已满足 我认为在我的人生结束时会有某种出口if声明 但不知道这将如何运作 Dim i As Long For i 1 To 50 Range B i Select
  • 使用 dataTable.js Bootstrap 表排序、过滤、分页

    我正在开发 asp net MVC 5 并创建了一个 Bootstrap 表来显示数据 现在我想应用分页 过滤和排序 为此我搜索了五篇文章并找到了这个链接 此链接中的技术非常简单 与我在项目中所做的相同 下面我已经包括了我的 js and
  • C 如何计算 sin() 和其他数学函数?

    我一直在研究 NET 反汇编和 GCC 源代码 但似乎找不到任何实际实现sin 和其他数学函数 它们似乎总是引用其他东西 谁能帮我找到他们吗 我觉得不太可能所有运行C的硬件都支持硬件中的三角函数 所以必须有一个软件算法某处 正确的 我知道有
  • “尽早失败”这句话是什么意思?您想什么时候这样做?

    尽早失败 这个表达是什么意思 在什么情况下这种方法最有用 什么时候你会避免这种方法 本质上 快速失败 又名早早失败 是对您的软件进行编码 以便 当出现问题时 软件会失败立刻 and 明显地如可能的 而不是试图在可能不稳定的状态下继续进行 快
  • 在Python中,如何将数字和字符串转换为字节数组?

    我想将一组配置选项编码为一长串十六进制数字 输入是数字 整数和浮点数 和字符串的混合 我可以用binascii a2b hex从字符串的标准库 整数的按位运算符 如果我去阅读一些关于浮点表示的内容 叹气 我可能也可以处理浮点数 现在 我的问
  • iOS Sprite Kit 碰撞与元素移动

    是否可以使用 Sprite 套件的物理主体进行元素碰撞并仍然允许元素相互穿过 我感兴趣的是仅当两个边界相互碰撞时才调用委托 然后允许它们相互穿过 是的 这是可能的 您可以注册对象之间碰撞的回调 但不会让碰撞影响对象本身 Raywenderl
  • 右键单击 Silverlight 4 应用程序中的列表框

    我正在尝试在 Winforms 应用程序中实现我以前认为理所当然的功能 我是一名 Silverlight 菜鸟 所以希望这一切都是基础知识 我在 Silverlight 4 应用程序中有一个列表框 我想做以下事情 右键单击列表框 让该项目位
  • 为什么 Promise 是 Monad?

    我一直在学习函数式编程 并且接触过 Monad Functor 和 Applicatives 根据我的理解 以下定义适用 a A gt B gt C A gt C B 函子 b A gt C B gt C A gt C B 单子 c C A