Gleam v1.17.0

Hacker News Top 工具

摘要

Gleam v1.17.0 引入了 `gleam export escript` 命令以创建单文件 BEAM 程序,在语言服务器中高亮引用,以及常量 `todo` 表达式。此外,首届 Gleam Gathering 大会的视频也已发布。

暂无内容
查看原文
查看缓存全文

缓存时间: 2026/06/03 00:35

# 使用 escript 编写单文件 Gleam BEAM 程序 | Gleam 编程语言 来源:https://gleam.run/news/single-file-gleam-beam-programs-with-escript/ Gleam 是一种类型安全且可扩展的语言,适用于 Erlang 虚拟机和 JavaScript 运行时。今天,Gleam v1.17.0(https://github.com/gleam-lang/gleam/releases/tag/v1.17.0)已发布。 ## Gleam Gathering 但首先是:首届全 Gleam 大会的首批视频已经发布!你可以在 Gleam Gathering YouTube 频道(https://www.youtube.com/@GleamGathering)上观看。这次活动非常成功且充满乐趣。敬请关注 2027 年下一届大会的消息!好了,回到版本发布的内容。 ## BEAM escript 在 Erlang 虚拟机上运行时,Gleam 代码会被编译成一系列 `.beam` 文件,每个文件包含单个 Gleam 模块的字节码。这对于通过包管理器、容器或其他系统分发或安装的程序来说很合适,但分享小型命令行程序时,拥有多个文件会有些不便。 在 JavaScript 世界中,这个问题通过“打包工具”解决,它能将多个 JavaScript 模块合并成一个文件。这个单一文件可以复制到任何安装了 JavaScript 运行时的计算机上运行(如 NodeJS、Deno 或 Bun)。Erlang 有类似的解决方案:escript。类似于 JavaScript 打包文件,escript 是一个单一文件,包含了程序的所有模块,以预编译字节码的形式存在,并且可以在任何安装了 Erlang 的计算机上运行。 Erlang 构建工具有一个方便的命令来创建 escript,但对于 Gleam 程序员来说,escript 的创建过程并不那么直接。本次发布增加了 `gleam export escript` 命令,它将编译项目,验证项目是否具有有效的 `main` 函数,并根据编译后的字节码构建 escript 文件。 `` louis ~/src/my_project $ gleam export escript Compiling gleam_stdlib Compiling my_project Compiled in 0.48s Your escript has been generated to /home/louis/src/my_project/my_project. ``` `` louis ~/src/my_project $ ./my_project Hello from my_project! `` ## 高亮引用 Gleam 的语言服务器为所有实现了语言服务器协议的编辑器提供了 IDE 功能。本次发布增加了对 `textDocument/documentHighlight` 特性的支持,可以高亮显示所选变量的所有引用。 例如,在以下代码中,当光标放在任何 `vec` 实例上时,将会触发这些高亮: `` fn to_cartesian(vec) { // ^^^ let x = vec.rho * cos(vec.theta) // ^^^ ^^^ let y = vec.rho * sin(vec.theta) // ^^^ ^^^ #(x, y) } `` 感谢 Gavin Morrow(https://github.com/gavinmorrow)的贡献! ## 常量中的 todo 表达式 Gleam 的 `todo` 关键字是一个占位符表达式,程序员可以在尚未完成的代码中使用,以便进行类型检查或运行。在编译时,它会输出一个警告,说明代码不完整,如果包含 `todo` 表达式的代码路径被执行,程序将崩溃并退出。 现在 `todo` 也可以在常量表达式中使用。由于常量表达式在编译时求值,当 `todo` 用于常量时,程序将无法运行,但仍然可以进行类型检查和分析。这也使我们能够升级“填充标签”代码操作,使其也能用于常量。 执行该操作时,它会填充记录构造器中缺失的带标签参数。例如: `` pub type Pokemon { Pokemon(number: Int, name: String, hp: Int) } pub const cleffa = Pokemon(number: 173) `` 在这个代码片段中,我们没有指定 `name` 和 `hp` 字段,这会报错!触发“填充标签”代码操作将得到如下结果: `` pub const cleffa = Pokemon(number: 173, name: todo, hp: todo) `` 感谢 Giacomo Cavalieri(https://github.com/giacomocavalieri)! ## 记录更新悬停 在编辑器中悬停是获取更多信息的好方法,语言服务器会显示被悬停元素的类型、文档和其他细节。Gleam 的记录更新语法用于从现有记录创建新记录,同时更新某些字段的值。当悬停在记录更新上时,语言服务器现在还会显示未赋予新值的剩余字段,从而省去了跳转到定义处查看还有哪些字段可以设置新值的麻烦。 `` pub type Person { Person(name: String, age: Int) } pub fn happy_birthday_mom() { let mom = Person(name: "Antonella", age: 60) Person(..mom, age: 61) // ^^^^^ 悬停在此处将显示: // 未更改的字段: // - name } `` 感谢 Giacomo Cavalieri(https://github.com/giacomacavalieri)! ## 未知值导入建议 在 Gleam 中,来自其他模块的函数通常以限定方式使用,写成 `dict.fold` 而不是 `fold`。这样做是为了让读者更清楚地知道函数定义的位置及其作用,并避免为函数名添加冗余后缀以指示其操作的类型。 有时候程序员可能会忘记写模块限定符,导致编译错误,因为作用域中没有该名称的值。此时编译器现在会在已导入的模块中搜索具有该名称的合适值,并作为位置修正进行建议。 例如,对于以下无效程序: `` import gleam/io pub fn main() -> Nil { println("Hello, World!") } `` 编译器将显示以下错误信息: `` error: Unknown variable ┌─ /path/to/project/src/project.gleam:4:3 │ 4 │ println("Hello, World!") │ ^^^^^^^ The name `println` is not in scope here. Did you mean one of these: - io.println `` 感谢 raphrous(https://github.com/realraphrous)! ## 警告中类型打印的上下文感知 到目前为止,当警告中显示类型名称时,使用的是其规范名称(canonical name),但这可能与程序员在代码中引用该类型的方式不同。类型可能以限定方式引用,如 `accounting.Invoice`,或者程序员可能为该类型取了别名,以便在代码上下文中更容易理解。 现在,警告中的类型会正确地进行限定或使用别名显示。例如,以下代码: `` import user as visitor pub fn main() { user.to_string(todo) |> io.println } `` 将产生以下警告: `` warning: Todo found ┌─ /src/warning/wrn.gleam:4:19 │ 4 │ user.to_string(todo) │ ^^^^ This code is incomplete This code will crash if it is run. Be sure to finish it before running your program. Hint: I think its type is `visitor.User`. `` 注意,类型提示正确地限定了该警告所在模块中的类型。 感谢 Giacomo Cavalieri(https://github.com/giacomocavalieri)! ## JavaScript 模式匹配优化 Gleam 的 `case` 表达式模式匹配看起来像从第一个到最后一个子句的线性搜索,但它会编译成高效的决策树,采用“分而治之”的方法来找到匹配的子句。 Daniele Scaratti(https://github.com/lupodevelop)和 Gavin Morrow(https://github.com/gavinmorrow)致力于进一步优化编译到 JavaScript 时的模式匹配代码生成,检测并移除处理位数组长度时的一些冗余检查,并使赋值代码更加紧凑。感谢你们! ## 安静开发模式 运行 Gleam 构建工具时,它会打印显示当前正在执行的操作的消息: `` louis ~/src/my_project $ gleam dev Compiling gleam_stdlib Compiling my_project Compiled in 0.48s Running my_project_dev.main Hello, from my_project_dev! `` `--no-print-progress` 标志可用于禁用这些信息,本次发布后,该标志已被 `gleam dev` 命令(以开发模式运行代码)接受。 `` louis ~/src/my_project $ gleam dev --no-print-progress Hello, from my_project_dev! `` 感谢 Giacomo Cavalieri(https://github.com/giacomocavalieri)! ## 过时依赖计数 `gleam deps outdated` 命令用于查看包的哪些依赖项有可以升级的新版本。本次发布后,它现在包含一个摘要,显示有多少包有可用的新版本。例如: `` louis ~/src/my_project $ gleam deps outdated 1 of 12 packages have newer versions available. Package Current Latest ------- ------- ------ gleam_stdlib 0.70.0 0.71.0 `` 当没有过时的包时,这也很有帮助,因为之前有些人因为没有任何输出而感到困惑,不确定是否有过时的依赖项。 `` louis ~/src/my_project $ gleam deps outdated 0 of 12 packages have newer versions available. `` 感谢 Daniele Scaratti(https://github.com/lupodevelop)! ## 更好的 Git 仓库检测 `gleam publish` 命令现在在 monorepo 中具有更好的 Git 仓库检测能力,即使要发布的 Gleam 包不在仓库的根目录下,也能正确识别该包是否在 Git 仓库中。 感谢 Andrey Kozhev(https://github.com/ankddev)! ## 更高的容错性 Gleam 的编译器实现了容错编译,即使代码处于无效状态,也能分析并推断出代码的信息。这很有用,因为这意味着语言服务器始终能够为程序员提供有用的帮助。 Giacomo Cavalieri(https://github.com/giacomocavalieri)改进了列表前置语法和记录更新语法中错误的容错性。感谢! ## 移除冗余记录更新的代码操作 语言服务器现在有了一个移除冗余记录更新的代码操作。例如: `` pub type User { User(name: String, likes: List(String)) } pub fn main() { let lucy = User(name: "Lucy", likes: ["Gleam", "Ice Cream"]) let jak = User(..lucy, name: "Jak", likes: ["Gleam", "Dogs"]) // ^^^^^^ 这个记录更新是不需要的! } `` 这个记录更新实际上并不需要,并且会引发警告;所有字段都已指定。在表达式的任意位置触发代码操作将移除不必要的更新: `` pub type User { User(name: String, likes: List(String)) } pub fn main() { let lucy = User(name: "Lucy", likes: ["Gleam", "Ice Cream"]) let jak = User(name: "Jak", likes: ["Gleam", "Dogs"]) } `` 感谢 Giacomo Cavalieri(https://github.com/giacomocavalieri)! ## 守卫表达式中正确运算符的代码操作 Gleam 的运算符没有重载,因此每个运算符都作用于特定类型。这有利于消除歧义,并帮助编译器可靠地推断程序中所有值的类型,而无需额外的类型标注。其他语言通过子类型来解决这个问题,但 Gleam 没有子类型,因此它使用无歧义的语法。 有时刚使用 Gleam 的程序员会混淆运算符,例如尝试使用 `+` 来拼接字符串,而不是 Gleam 的 `<>`。当发生这种情况时,Gleam 会向程序员建议正确的运算符,并提供语言服务器的自动修复操作来纠正它。 现在,这也适用于守卫表达式,因此可以在以下代码中错误的 `+` 上触发该操作: `` pub fn categorise() { case pokemon { Pokemon(name:, ..) if name == "rai" + "chu" -> todo _ -> todo } } `` 感谢 Giacomo Cavalieri(https://github.com/giacomocavalieri)! ## 对丢弃值进行模式匹配的代码操作 Gleam 实现了穷尽性检查(exhaustiveness checking),因此如果程序未能覆盖所有可能的边界情况,编译器会在编译期间引发有用的错误,向程序员展示遗漏了什么。编译器为这个特性从代码中收集的信息还使其能够提供一些非常方便的省时语言服务器代码操作,例如“添加缺失的模式”和“对值进行模式匹配”。 最新的一个是展开 case 表达式中的丢弃模式(discard pattern),将其替换为匹配所丢弃内容的子句。 `` pub fn list_names(x: Result(List(String), Nil)) { case x { Error(Nil) -> io.println("no names") Ok(_) -> todo // ^ 在此触发代码操作 } } `` 这里,`_` 丢弃模式用于匹配任何列表。在该模式上触发代码操作会将该子句展开为两个子句,使用列表最常用的模式: `` pub fn list_names(x: Result(List(String), Nil)) { case x { Error(Nil) -> io.println("no names") Ok([]) -> todo Ok([first, ..rest]) -> todo } } `` 感谢 Giacomo Cavalieri(https://github.com/giacomocavalieri)! ## 创建未知模块的代码操作 世界上有两种程序员:自顶向下和自底向上。自顶向下的程序员从宏观蓝图开始,逐步分解为越来越小的部分,递归实现直到最底层。自底向上的程序员从最小的构建块开始,逐步组合直到形成完整的系统。 本次发布带来了一个新的代码操作,可能特别受自顶向下程序员的欢迎:创建模块。只要导入了一个不存在的模块,就可以触发此操作,并且运行后会在程序员的包中创建缺失的模块。 例如,如果在 `src/wiggle.gleam` 中添加了 `import wobble/woo`,那么在 `import wobble/woo` 上触发代码操作时,将会出现创建 `src/wobble/woo.gleam` 的选项。 感谢 Cory Forsstrom(https://github.com/tarkah)! ## 安全修复 安全研究人员 Abdelrahman Ahmed Aboelkasem(https://github.com/0x2face)、Aly(https://github.com/spect3r1)和 evipepota(https://github.com/evipepota)发现了 Gleam 构建工具中一些与配置验证相关的漏洞。值得庆幸的是,这些漏洞并不十分可怕,并且现在已经修复。 详情请参见 CVE-2026-43965(https://github.com/gleam-lang/gleam/security/advisories/GHSA-jqvf-f6p2-wrv3)、CVE-2026-32685(https://github.com/gleam-lang/gleam/security/advisories/GHSA-wjx8-7w8m-p4v7)和 CVE-2026-42795(https://github.com/gleam-lang/gleam/security/advisories/GHSA-qhh5-fg4c-8gqc)。 Erlang 生态系统基金会(https://erlef.org/)的安全团队在保护整个 Erlang 生态系统方面做出了宝贵贡献,尤其是在基于人工智能的漏洞检测工具越来越有效的今天。如果你或你的公司有能力,请向他们提供支持。感谢所有研究人员和修复人员! ## 更多内容! 衷心感谢所有的 Bug 修复者和体验优化者:Andrey Kozhev(https://github.com/ankddev)、apsoras(https://github.com/apsoras)、Charlie Tonneslan(https://github.com/c-tonneslan)、daniellionel01(https://github.com/daniellionel01)、Eyup Can Akman(https://github.com/eyupcanakman)、Gavin Morrow(https://github.com/gavinmorrow)、Giacomo Cavalieri(https://github.com/giacomocavalieri)、John Downey(https://github.com/jtdowney)、Logan Bresnahan(https://github.com/LoganBresnahan)、Sahil Upasane(https://github.com/404salad)和 Surya Rose(https://github.com/GearsDatapacks)。 有关他们实施的许多修复和改进的完整详细信息,请参见变更日志(https://github.com/gleam-lang/gleam/blob/main/changelog/v1.17.md)。 ## 支持 Gleam Gleam 并非来自大型科技公司,也未接受任何风险投资。我们完全依靠社区来维持生计。我们在实现能够适当支付核心团队成员工资的目标上取得了很大进展,但仍有很长的路要走。请考虑通过 GitHub Sponsors 支持该项目(https://github.com/sponsors/lpil)或核心团队成员 Giacomo Cavalieri(https://github.com/sponsors/giacomocavalieri)和 Surya Rose(https://github.com/sponsors/GearsDatapacks)。 GitHub Sponsors(https://github.com/sponsors/lpil)感谢所有赞助者!

相似文章

用于 Gleam 单仓库的 GitHub Actions

Lobsters Hottest

一位开发者分享了他们在 Gleam 单仓库中测试 BEAM 与 JavaScript 两套运行时的 GitHub Actions 配置,采用矩阵策略并严格执行格式检查。

Core Team Panel - Gleam Gathering 2026

Lobsters Hottest

Gleam 核心团队在 2026 年聚会上分享了个人故事、2026 年计划、社区友好氛围的维持方式,以及对 AI 编码工具的看法。

EYG:大量CLI改进、新指南和新效果

Lobsters Hottest

EYG CLI 新增了命令(eval、check、script、shell)、标志、通过 glam 库实现的漂亮打印、改进的错误输出、更简便的安装,以及新的效果,如 DeleteFile、Env、Hash、Now、Random、Sleep。同时发布了新的指南。