Reasonml 中 -> 和 |> 有什么区别?

2024-01-03

经过一段时间的激烈谷歌搜索,我得到了一些例子,人们在一个代码中使用两种类型的运算符,但通常它们看起来就像做一件事的两种方式,它们甚至具有相同的名称


tl;dr:决定性的区别在于->管道到第一个参数,同时|>管道到最后。那是:

x -> f(y, z) <=> f(x, y, z)
x |> f(y, z) <=> f(y, z, x)

不幸的是,有一些微妙之处和含义使得这在实践中变得更加复杂和混乱。请耐心听我解释其背后的历史。

管道时代之前

在出现任何管道运算符之前,大多数函数式程序员都将函数操作的“对象”作为最后一个参数来设计大多数函数。这是因为使用部分函数应用可以使函数组合变得更加容易,并且如果未应用的参数位于末尾,则在柯里化语言中,部分函数应用会变得更加容易。

Currying

在柯里化语言中,每个函数都只接受一个参数。看似接受两个参数的函数实际上是一个接受一个参数的函数,但随后返回另一个接受另一个参数的函数,进而返回实际结果。因此这些是等价的:

let add = (x, y) => x + y
let add = x => y => x + y

或者更确切地说,第一种形式只是第二种形式的语法糖。

部分功能应用

这也意味着我们只需提供第一个参数就可以轻松地部分应用函数,这将使其返回一个在生成结果之前接受第二个参数的函数:

let add3 = add(3)
let result = add3(4) /* result == 7 */

如果没有柯里化,我们就必须将其包装在一个函数中,这要麻烦得多:

let add3 = y => add(3, y)

巧妙的功能设计

现在事实证明,大多数函数都对“主”参数进行操作,我们可以将其称为函数的“对象”。List函数通常对特定列表进行操作,例如,不会同时操作多个列表(当然,这种情况也会发生)。因此,将主要参数放在最后可以使您更轻松地编写函数。例如,通过几个精心设计的函数,定义一个函数将可选值列表转换为具有默认值的实际值列表非常简单:

let values = default => List.map(Option.defaultValue(default)))

虽然首先使用“对象”设计的函数需要您编写:

let values = (list, default) =>
  List.map(list, value => Option.defaultValue(value, default)))

管道时代的黎明(讽刺的是,这并不是管道优先)

据我了解,有人在 F# 中发现了一种常见的管道模式,并认为为中间值提供命名绑定或使用太多该死的括号以向后顺序嵌套函数调用都很麻烦。所以他发明了管道转发运算符,|>。这样,管道可以写成

let result = list |> List.map(...) |> List.filter(...)

代替

let result = List.filter(..., List.map(..., list))

or

let mappedList = List.map(..., list)
let result = List.filter(..., mapped)

但这仅在主要参数位于最后时才有效,因为它依赖于通过柯里化的部分函数应用。

然后...BuckleScript

Then along comes Bob, who first authored BuckleScript in order to compile OCaml code to JavaScript. BuckleScript was adopted by Reason, and then Bob went on to create a standard library for BuckleScript called Belt. Belt ignores almost everything I've explained above by putting the main argument first. Why? That has yet to be explained, but from what I can gather it's primarily because it's more familiar to JavaScript developers1.

Bob did recognize the importance of the pipe operator, however, so he created his own pipe-first operator, |., which works only with BuckleScript2. And then the Reason developers thought that looked a bit ugly and lacking direction, so they came up with the -> operator, which translates to |. and works exactly like it... except it has a different precedence and therefore doesn't play nice with anything else.3

结论

A pipe-first operator isn't a bad idea in itself. But the way it has been implemented and executed in BuckleScript and Reason invites a lot of confusion. It has unexpected behavior, encourages bad function design and unless one goes all in on it4, imposes a heavy cognitive tax when switching between the different pipe operators depending on what kind of function you're calling.

因此,我建议避免使用管道优先运算符(-> or |.) 并使用管道转发 (|>)与一个占位符参数 https://reasonml.github.io/docs/en/pipe-first#pipe-placeholders(也是 Reason 独有的)如果您需要通过管道传输到“对象”优先的函数,例如list |> List.map(...) |> Belt.List.keep(_, ...).


1 There are also some subtle differences with how this interacts with type inference, because types are inferred left-to-right, but it's not a clear benefit to either style IMO.

2 Because it requires syntactic transformation. It can't be implemented as just an ordinary operator, unlike pipe-forward.

3 For example, list |> List.map(...) -> Belt.List.keep(...) doesn't work as you'd expect https://stackoverflow.com/q/54957566/7943564

4 Which means being unable to use almost every library created before the pipe-first operator existed, because those were of course created with the original pipe-forward operator in mind. This effectively splits the ecosystem in two.

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

Reasonml 中 -> 和 |> 有什么区别? 的相关文章

  • 为什么 OCaml 有时需要 eta 扩展?

    如果我有以下 OCaml 函数 let myFun CCVector map 1 它在 Utop 中运行良好 并且 Merlin 不会将其标记为编译错误 然而 当我尝试编译它时 出现以下错误 错误 该表达式的类型 int a CCVecto
  • C# TPL 数据流 - 完成不起作用

    此代码永远不会到达最后一行 因为完成不会从 saveBlock 传播到 sendBlock 我究竟做错了什么 var readGenerateBlock new TransformBlock
  • 如何让 ocaml 相信两个函子实例化是相等的

    假设我有许多模块 它们都使用一种模块类型进行参数化 并且彼此之间也具有依赖关系 module type AT sig end module B A AT struct module Hash struct type t int let eq
  • Bash 错误:需要整数表达式

    在下面的部分中 您将看到我尝试在 UNIX 计算机上运行的 shell 脚本以及脚本 当我运行这个程序时 它给出了预期的输出 但它也给出了记录中显示的错误 可能是什么问题以及如何解决它 首先 脚本 usr bin bash while re
  • 在ocaml中编写多行函数

    我无法理解如何在 ocaml 中编写函数 因为我只编写了不需要用 分隔的多行的递归函数 我正在尝试创建一个函数 给定一个整数 n 返回一个充满零且对角线上只有一个的矩阵 因此大小为 n 的单位矩阵 我是函数式编程和 ocaml 的新手 所以
  • MonadFix 用严格的语言

    我正在为 Ocaml 中类似 haskell 的 do 表示法开发 camlp4 扩展 并试图弄清楚 GHC 如何编译递归 do 绑定 使用 XDoRec 启用 我想知道一元定点组合器是否可能以严格的语言存在 如 Ocaml F SML 如
  • OCaml 的并行化能力状况如何?

    我对在项目中使用 OCaml 很感兴趣 但是我不确定它的并行化功能在哪里 OCaml中有消息传递能力吗 OCaml 是否能够有效地使用 1 个以上的 CPU 我读到的关于这个主题的大部分内容都是在 2002 年至 2006 年写的 我没有看
  • Gitlab CI 同时在多个平台上运行

    我有一个针对多个操作系统 Linux Windows MacOS 以及多个 CPU 架构 i386 x86 64 arm Aarch64 编译和打包的 C 项目 为此 我使用 Jenkins 获取源代码并在每个系统上并行运行构建脚本 这是一
  • 使用 Opam 管理项目依赖关系

    我是 OCaml 的新手 我使用过的其他语言 例如 Scala Clojure Node js 上的 Javascript 都有包管理器 允许人们以干净的状态启动项目 该项目具有一组已声明的已知版本的依赖项 我正在尝试与 Opam 做类似的
  • 跨编译单元的 OCaml 递归模块

    我试图将以下递归模块拆分为单独的编译单元 具体来说 我希望 B 位于它自己的 b ml 中 以便能够与其他 A 一起重用它 module type AT sig type b type t Foo of b Bar val f t gt b
  • 是否可以在 OCaml 解释器中使用箭头键?

    每次我在解释器中使用这些键时 我都会不断出现如下符号 D C 我在 ZSH 中使用 Linux Mint 12 但是在 Ubuntu 中使用 bash 得到了相同的结果 另外 ssh 中也是同样的情况 库存 OCaml 顶层没有内置行编辑功
  • Gitlab 管道 - 报告配置包含未知键:cobertura

    由于此错误 我无法运行 gitlab 管道 Invalid CI config YAML file jobs run tests artifacts reports config contains unknown keys cobertur
  • 如何从ocaml列表中获取子列表

    我正在查看列表文档 图书馆好像没有提供sublist功能 我正在尝试从中获取元素列表i to j 现在我必须把它写成 let rec sublist list i j if i gt j then else List nth list i
  • OCaml 数据类型定义中的方括号“[”和“]”是什么意思?

    I saw 下列 https coq github io doc v8 11 api coq Genarg index html type rlevel type rlevel rlevel 但我以前从未见过这种情况 并且 ADT 代数数据
  • 类型变量和局部抽象类型有什么区别?

    我试图理解的目的局部抽象类型在 OCaml 中 局部抽象类型与类型变量有何不同 看来他们有相同的行为 Type variable let f x a a x val f a gt a
  • Azure devops:将变量组作为参数传递给模板

    我在代码库中使用 Azure devops yml 管道 我在管道中创建了变量组 管道 gt 库 gt 变量组 gt 称为 MY VG 在我的管道 yml 文件中 我想将此变量组 MY VG 作为参数发送到模板 my template ym
  • 如何在PowerShell中从外部命令输出获取原始二进制数据?

    我在这里读到 当您在 powershell 中运行外部命令时 它们的输出始终被解释为字符串或字符串数 组 https stackoverflow com a 35980675 983442 https stackoverflow com a
  • 如何在 OCaml 中使协变可观察

    我正在尝试为值制作一个包装器 允许调用者自行注册以获取有关它的通知 这是一些 工作 代码 module Thing sig type a t val make a gt a t val watch a gt unit gt a t gt u
  • 有人可以解释一下这个 OCaml 程序中使用的类型语法吗?

    以下类型取自这个问题 https stackoverflow com q 50586942 633183 contains an error later fixed by the OP type task Success a gt a ta
  • 属性错误:未找到下层;在 scikit-learn 中使用带有 CountVectorizer 的 Pipeline

    我有一个这样的语料库 X train this is an dummy example in reality this line is very long here is a last text in the training set 和一

随机推荐

  • 为什么这个 std::vector 会给出运行时错误?

    vector
  • .NET:关于 AssemblyVersion,什么定义了二进制兼容性?

    对强命名程序集的哪些更改需要更改 AssemblyVersionAttribute 显然 以可能要求客户端必须更改代码的方式更改公共 api 需要增加 AssemblyVersion 但是 如果对公共 API 进行更改而不需要在客户端中更改
  • C 中的类型双关和联合

    我目前正在开发一个项目来构建一个小型编译器 只是为了它 我决定采用构建一个极其简单的虚拟机作为目标的方法 这样我就不必担心学习 elf intel 汇编等的细节 我的问题是关于 C 中使用联合的类型双关 我决定在虚拟机内存中仅支持 32 位
  • MongoDB嵌套对象聚合计数

    我有一个高度嵌套的 mongoDB 对象集 我想计算与给定条件匹配的子文档的数量编辑 在每个文档中 例如 id chr 20 pos 14371 ref A alt G studies study id Study1 samples sam
  • RuntimeError:DataLoader 工作线程意外退出

    我是 PyTorch 和机器学习的新手 所以我尝试按照这里的教程进行操作 https medium com nutanbhogendrasharma pytorch 卷积神经网络 with mnist dataset 4e8a4265e11
  • 使用 Emscripten 将 R 函数编译为 JavaScript

    我正在尝试使用 Emscripten 将一些用 C 编写的 R 函数编译为 JavaScript 我的第一个任务是移植一个名为pf 源码可以找到here https github com wch r source blob trunk sr
  • PHP:递归地将目录中的所有文件重命名为小写

    我需要帮助 我想递归地将目录中的所有文件重命名为小写 我有一个代码要测试 但它仅在该文件夹内重命名 而不是递归地重命名 我怎样才能让它递归地执行呢 这是我使用的代码 您可以使用 SPL递归目录迭代器 http docs php net ma
  • 不同组的 dplyr 滞后

    我正在尝试使用 dplyr 来改变包含变量的同一组滞后的列以及其他组 之一 的滞后 编辑 抱歉 在第一版中 我在最后一秒按日期重新排列 有点搞乱了顺序 这就是我想要的结果 Here is a minimal code example lib
  • Django - 复制具有 2 个嵌套外键的模型实例

    我是 django 新手 我有一个调查应用程序 其中管理员创建有问题的调查 问题有选择 我已将 save as True 添加到我的调查管理员中 但是当我复制调查时 问题是存在于副本中 但不存在选择 class SurveyAdmin ad
  • Pandas 列数学运算 无错误无答案

    我正在尝试对文件执行一些简单的数学运算 下面的列file 1 csv本质上是动态的 列的数量会不时增加 所以我们无法修复last column master ids csv 在任何预处理之前 Ids ref0 the columns inc
  • CMake编译时生成源代码

    我在使用CMake时遇到以下情况 它必须构建两个应用程序 a 发电机 b 其他的东西 The 发电机没什么花哨的 几个 CPP 文件链接在一起 The 其他的东西 is A 一些 正常 CPP 文件 b 生成的 CPP h 文件 我必须链接
  • VS Code / Metals:如何使用远程调试器调试 Scala sbt 项目?

    我知道这个问题之前已经以某种形式被问过here https stackoverflow com questions 60495320 how to debug scala sbt project in vs code 但我根本不知道如何在
  • Rails 找不到 rake gem

    我有一个在 docker 中运行的基本 Rails 应用程序 我可以很好地启动它并提供响应 我可以跑rake T正好 但我无法运行 Rails 生成命令 它无法告诉我Could not find rake 12 3 1 in any of
  • CSS - 缩进列表项

    有没有办法使用 CSS 来缩进每个列表项 所以一个正常的列表 ul li One li li Two li li Three li li Four li ul 显示如下 One Two Three Four 在这里你可以使用 before具
  • 如何在java中将UUID保存为二进制(16)

    我有一个表 TestTable 其列 ID 为 binary 16 名称为 varchar 50 我一直在尝试将有序的 UUID 存储为 PK 就像本文中一样以优化的方式存储 UUID https www percona com blog
  • 使用 Restify 提供静态文件 (node.js)

    我有以下代码 app js server get docs public restify serveStatic directory public server listen 1337 function console log s list
  • AngularJS、PHP Restful Cors 问题

    我在尝试对我的其余 php 服务器进行 http 调用时遇到问题 我正在进行从客户端到后端的跨域调用 在我的 Angular 应用程序中 http 服务的配置方式如下 config httpProvider function httpPro
  • 查找 f(x)=0 处的阈值

    我有一个函数f x 这是正数并且递减x
  • iPhone:以编程方式将选项卡栏控制器添加到导航控制器

    我正在开发一个 iPhone 应用程序 我从基于导航的模板开始 但现在我想在底部添加一个 Tap Bar 控制器 并且我想在不使用 Interface Builder 的情况下执行此操作 有人能告诉我该怎么做吗 提前致谢 框架不支持这一点
  • Reasonml 中 -> 和 |> 有什么区别?

    经过一段时间的激烈谷歌搜索 我得到了一些例子 人们在一个代码中使用两种类型的运算符 但通常它们看起来就像做一件事的两种方式 它们甚至具有相同的名称 tl dr 决定性的区别在于 gt 管道到第一个参数 同时 gt 管道到最后 那是 x gt