
编译器编程语言开发工具【免费下载链接】rescript-compilerReScript is a robustly typed language that compiles to efficient and human-readable JavaScript.项目地址https://gitcode.com/gh_mirrors/re/rescript-compiler点击查看免费下载Belt 是 ReScript 官方推出的扩展工具与集合库与编译器、标准库分开独立发布专注于提供性能优先、以不可变集合为核心的各类数据结构和常用数据类型的工具函数。本文以 packages/rescript/belt/README.md 为主体结合其源码注释与 API 签名完整讲解 Belt 的安装配置、open Belt的三种使用方式、模块全貌、专化集合以及数组越界安全与基于 phantom type 的集合身份编码两大实现细节帮助读者在真实项目中安全、高效地使用 Belt。Belt 是什么定位与设计动机Belt 是一套面向 ReScript 的扩展工具与集合库其核心定位见 Belt.res 源码注释包含四点高质量的不可变数据结构为 JavaScript 平台提供经过优化的集合类型重点强调不可变集合immutable collections默认安全除非函数名明确带有Exn后缀Belt 函数保证不抛异常性能与体积在 JS 平台上追求更好的性能与更小的代码体积并对 Tree Shaking 友好独立分发Belt 与编译器、标准库分离通过 npm 包rescript/belt单独安装。在 package.json 中可以看到它的依赖关系rescript/belt以rescript与rescript/runtime为 peer 依赖即它依赖 ReScript 编译器与运行时但本身是独立的库包。安装与配置三步接入项目1. 安装 npm 包npm install rescript/belt2. 在rescript.json中声明依赖{ dependencies: [rescript/belt] }3. 编译与使用Belt 源码位于 packages/rescript/belt/src其自身 rescript.json 同时输出 CommonJS.cjs与 ES Module.mjs两种格式并使用-bs-cross-module-opt、-unsafe等编译标志优化自身产物。用户在项目中添加依赖后即可通过rescript build正常编译使用。仓库内的 Belt 测试工程 tests/belt_tests/rescript.json 展示了真实项目中的依赖声明方式在dependencies数组中列出rescript/belt随后便可在src下的源码中直接引用 Belt 模块。三种使用方式全限定名、文件内 open、全局 openBelt 不会自动打开not opened automatically官方提供了三种渐进式使用方式方式一全限定名引用推荐起点始终通过完整模块名引用无需任何额外配置Belt.Array.map([1, 2, 3], x x * 2) Belt.Map.get(map, key)方式二文件内open Belt在需要使用短模块名的源文件顶部写入open Belt此后该文件中的Array指Belt.ArrayList指Belt.List依此类推。注意open Belt之后数组下标访问letters[0]会解析为Belt.Array.get返回option而非直接抛出异常——这一点正是下文数组访问运行时安全所讨论的核心行为差异。方式三全局-open Belt若希望整个项目的所有文件都默认可用短模块名在rescript.json中配置{ compiler-flags: [-open Belt] }重要提示源码注释中明确强调-open Belt是官方唯一鼓励的全局open方式this is theonlyopen we encourage。这保证了Array、List、Map、Set等常见名称在全项目中拥有一致的语义避免不同库的open相互冲突。模块全景集合与工具函数一览从 Belt.res 的模块映射可以看出Belt顶层聚合了 16 个模块可划分为三类不可变集合模块说明Belt.Array可变数组的工具函数如get/set/keep/reverseBelt.List链表List数据类型的工具函数Belt.Map通用不可变 Map含Int、String、Dict三个内嵌专化模块Belt.Set通用不可变 Set含Int、String、Dict三个内嵌专化模块Belt.SortArray通用排序工具含Int、String专化模块Belt.Range闭区间(from, to)的工具函数Belt.Option/Belt.Resultoption与result数据类型的工具函数Belt.Int/Belt.Float整型与浮点数的工具函数可变集合模块说明Belt.MutableMap通用可变 Map含Int、String专化模块Belt.MutableSet通用可变 Set含Int、String专化模块Belt.MutableQueueFIFO先进先出队列Belt.MutableStackLIFO后进先出栈哈希集合模块说明Belt.HashMap通用可变哈希 Map含Int、String专化模块Belt.HashSet通用可变哈希 Set含Int、String专化模块Belt.Id创建带唯一身份的 comparator / hash 的工具模块一个完整示例管道式去重排序以下示例来自 Belt.res 源码注释展示了 Belt 的典型组合用法——先用Belt.Array.keep过滤再通过Belt.Set.Int去重Set 天然有序最后转回数组得到有序结果let someNumbers [1, 1, 4, 2, 3, 6, 3, 4, 2] let greaterThan2UniqueAndSorted someNumbers -Belt.Array.keep(x x 2) // convert to and from set to make values unique -Belt.Set.Int.fromArray -Belt.Set.Int.toArray // output is already sorted Console.log2(result, greaterThan2UniqueAndSorted)这里用到了专化集合Specialized Collections的核心思想Belt.Set.Int是针对int键优化的实现比通用Belt.Set更高效。Belt 对 Set、Map 等集合类型同时提供通用模块与int/string 键专化模块Belt.Set/Belt.Set.Int/Belt.Set.StringBelt.Map/Belt.Map.Int/Belt.Map.String可变版本Belt.MutableSet、Belt.MutableMap同样提供Int、String专化模块这些专化模块在 Belt.res 源码 中一一映射到独立的实现文件例如Belt_SetInt、Belt_MapString、Belt_HashSetInt、Belt_HashMapString等。以 Belt_Map.resi 为例通用 Map 的类型签名是tkey, value, identity而内嵌的Int、String、Dict模块则对应Belt_MapInt、Belt_MapString、Belt_MapDict。其中Dict模块把数据与函数分离写法更啰嗦但性能略优。实现细节一数组访问的运行时安全Belt 对数组访问的处理与 ReScript 默认标准库不同这是最常见的困惑点。默认行为下越界访问会直接抛异常let letters [a, b, c] let a letters[0] // a a let capitalA String.toUpperCase(a) let k letters[10] // Throws an exception! The 10th index doesnt exist.而open Belt之后同样的代码行为完全不同——Belt 避免抛异常改用option表达结果open Belt let letters [a, b, c] let a letters[0] // a Some(a) let capitalA String.toUpperCase(a) // Type error! This code will not compile. let k letters[10] // k None这里发生了三件事源码注释明确列出ReScript 编译器会把数组下标访问转换desugar为对Array.get的函数调用即letters[0]等价于Array.get(letters, 0)编译器使用当前作用域内的Array模块——一旦open Belt就解析为Belt.ArrayBelt.Array.get将值包装在option中返回因此letters[0] Some(a)越界时得到None而非异常。这一点在 Belt_Array.resi 的 API 签名中得到印证let get: (ta, int) optiona越界返回None永不抛异常let getExn: (ta, int) a越界时抛出异常let getOrThrow: (ta, int) a与getExn等价的显式命名external getUnsafe: (ta, int) a %array_unsafe_get不做边界检查的 unsafe 原语。默认安全的约定由此可总结为一条规则不带Exn后缀的 Belt 函数不会抛异常需要异常语义时使用带Exn/OrThrow后缀的变体追求极致性能且能自行保证边界时才使用Unsafe变体。由于a现在是optionstring直接传给String.toUpperCase会得到类型错误。修复方式是显式解包open Belt let letters [a, b, c] let a letters[0] // Use a switch statement: let capitalA switch a { | Some(a) Some(String.toUpperCase(a)) | None None } let k letters[10] // k None经过这样的小改动代码既通过类型检查又 100% 消除了运行时越界异常。实现细节二集合安全的特殊编码phantom type当为自定义数据类型创建集合时Belt 需要一种机制来携带比较函数comparator。考虑以二元组(int, int)为元素的 SetSet 必须使用自定义的cmp函数来确定两个元组的顺序。如果 Set 只被类型化为Set.t(int * int)那么用户再创建一个不同的cmp函数时两个集合就可能混用、进而产生运行时错误。Belt 的解决方案是用phantom type幻影类型把比较函数的身份编码进集合的类型签名中。以 Belt.res 源码注释 中的例子说明module Comparable1 Belt.Id.MakeComparable({ type t (int, int) let cmp ((a0, a1), (b0, b1)) switch Pervasives.compare(a0, b0) { | 0 Pervasives.compare(a1, b1) | c c } }) let mySet1 Belt.Set.make(~idmodule(Comparable1)) module Comparable2 Belt.Id.MakeComparable({ type t (int, int) let cmp ((a0, a1), (b0, b1)) switch Pervasives.compare(a0, b0) { | 0 Pervasives.compare(a1, b1) | c c } }) let mySet2 Belt.Set.make(~idmodule(Comparable2))尽管Comparable1与Comparable2的内容看起来完全一致编译器仍会推断出mySet1与mySet2具有不同的类型——因此对这两个集合执行merge等操作会在编译期正确失败let mySet1: t(int, int), Comparable1.identity %todo let mySet2: t(int, int), Comparable2.identity %todoComparable1.identity与Comparable2.identity在该编码方案下不是同一个类型。这种运行时不带任何额外数据、仅在类型层面签名的编码方式正是 Belt_Id.resi 所定义的机制type cmpa, id运行时表示是一个cmp函数但通过类型参数id签名使不同比较函数在类型上互不兼容module type Comparable由type identity、type t与let cmp组成type comparablekey, id module(Comparable with type t key and type identity id)一个模块即身份的类型Belt.Id.MakeComparable创建带唯一身份的 comparable 模块而旧的MakeComparableU已标记deprecated(Use MakeComparable instead)。同样的思路也应用于哈希集合Belt.Id.hashable(~eq, ~hash)创建带身份的哈希函数。在 belt_hashmap_test.res 中可以看到真实用法——先用Belt.Id.hashable构造带身份的哈希模块再传给Belt.HashMap.make(~hintSize, ~id)。在仓库中继续深挖源码与测试地图如果想进一步研究 Belt 的实现与行为仓库内提供了完整的源码与测试模块实现packages/rescript/belt/src 下每个.res/.resi文件对应一个 Belt 模块.resi中的文档注释本身就是最好的 API 参考含丰富的Examples代码块。内部数据结构Belt_internalAVLtree/Belt_internalAVLsetAVL 树实现支撑 Set/Map、Belt_internalBuckets/Belt_internalSetBuckets哈希桶实现支撑 HashSet/HashMap。行为验证测试tests/belt_tests/src 下的bs_poly_set_test.res、bs_poly_map_test.res、bs_set_int_test.res、bs_map_test.res、bs_queue_test.res、bs_stack_test.res、belt_hashmap_test.res等分别覆盖通用多态集合、int 专化集合、队列、栈与哈希表的行为array_data_util.res、test_array.res等则覆盖数组工具函数。例如bs_poly_set_test.res验证了多态 Set 基于Belt.Id.MakeComparable创建并使用belt_hashset_int_test.res验证Belt.HashSet.Int专化模块的增删查行为。这些测试同时给出了各 API 在真实代码中的调用形态是学习 Belt 的最佳范例。小结Belt 作为 ReScript 的官方集合库围绕默认安全、性能优先、不可变优先三个原则设计安装只需npm install rescript/belt并在rescript.json声明依赖使用时可选全限定名、文件内open Belt或全局-open Belt通用模块与 int/string 专化模块并存兼顾灵活与性能数组访问通过option返回值消除越界异常自定义集合通过 phantom type 将比较函数身份编码进类型系统从编译期杜绝集合混用。掌握这些要点即可在项目中放心使用 Belt写出既高效又无运行时异常恐慌的 ReScript 代码。赞分享编译器编程语言开发工具【免费下载链接】rescript-compilerReScript is a robustly typed language that compiles to efficient and human-readable JavaScript.项目地址https://gitcode.com/gh_mirrors/re/rescript-compiler点击查看免费下载相关推荐vanilla-extract × Next.js 集成实战从安装配置到源码级原理vanilla extract × Next.js 集成实战从安装配置到源码级原理 vanilla extract 是一套 Zero runtime零运行时前端开发工具GeoPy地理编码库全面指南从安装到实战应用GeoPy地理编码库全面指南从安装到实战应用 什么是GeoPy GeoPy是一个强大的Python地理编码工具库它提供了与多种地理编码服务交互的统一接口。GISGeoPy地理编码库全面指南从安装到实战应用GeoPy地理编码库全面指南从安装到实战应用 什么是GeoPy GeoPy是一个强大的Python地理编码工具库它提供了与多种地理编码服务交互的统一接口。GIS上一篇NovelWriter字数统计机制深度解析从文本预处理到精准计数下一篇Angular-Cache 版本升级迁移指南从1.x到2.0的重大变更解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考