Gleam 1.7.0 版本深度解析:编译器优化、语言服务器代码动作与构建工具改进全览
【免费下载链接】gleam⭐️ A friendly language for building type-safe, scalable systems!项目地址: https://gitcode.com/GitHub_Trending/gl/gleam
导读
本文以 Gleam 编译器官方发布记录 changelog/v1.7.md 为核心骨架,系统梳理 Gleam 1.7.0(2025-01-05 发布)在编译器、构建工具、语言服务器、格式化器与文档生成五个维度的全部变更。通过结合本仓库(Gleam 编译器与语言服务器的 Rust 源码)中的实现与测试用例,你将掌握let assert自定义 panic 消息、按变体@deprecated、记录更新泛型参数变更、空块语义、语言服务器五大新代码动作等核心特性的语法、适用场景与底层原理,可直接用于升级与日常开发。
1. 版本发布概况
1.1 版本时间线与命名
Gleam 1.7.0 于2025-01-05正式发布,此前经历了三个候选版本迭代:
| 版本 | 发布日期 | 定位 |
|---|---|---|
| 1.7.0-rc1 | 2024-12-29 | 编译器与构建工具主要特性 |
| 1.7.0-rc2 | 2024-12-30 | 修复 JavaScript 位数组切片模式匹配 bug |
| 1.7.0-rc3 | 2025-01-02 | 格式化器改进与代码动作修复 |
| 1.7.0 | 2025-01-05 | 正式发布 |
说明:本文所引用的代码位置均来自当前仓库(Gleam 编译器源码),对应 1.7.0 及之后持续演进的版本,行号以当前仓库为准。
1.2 版本语义
本版本不涉及语言语法层面的破坏性变更,属于向后兼容的特性版本。唯一需要注意的软性变更:erl与js这两个目标平台简写名在@target属性等场景中被标记为弃用(deprecated),编译器会给出提示,但短期内仍可用。
2. 编译器新特性与优化
2.1let assert支持自定义 panic 消息
在 Gleam 中,let assert用于对值进行断言解构,失败时触发运行时 panic。1.7.0 之前 panic 消息是固定的;现在可以使用as关键字附加自定义消息:
let assert Ok(regex) = regex.compile("ab?c+") as "This regex is always valid"- 语法:
let assert Pattern = expr as "自定义消息"; - 行为:当模式匹配失败时,运行时 panic 将携带这条自定义消息,便于快速定位断言失败的上下文;
- 前提:自定义消息仅适用于非穷尽模式的
let assert(即可能失败的断言)。若模式必然匹配(如let assert Ok(10) = Ok(20) as "..."),编译器会给出"断言永不会失败"之类的警告。
源码印证:该特性在编译器的 Erlang 与 JavaScript 目标测试中均有覆盖,例如 let_assert.rs 中的message用例,以及 assignments.rs 中let assert Ok(inner) = value as "Oops, there was an error"的测试;类型检查器在 warnings.rs 中也对"带消息的let assert匹配必然成立"这一情况给出警告(对应快照 let_assert_with_message_requires_v1_7.snap)。
2.2 记录更新允许变更泛型类型参数
此前 Gleam 的记录更新语法Record(..existing, field: new_value)要求更新前后记录的类型参数完全一致。1.7.0 放宽了限制:记录更新现在可以改变记录的泛型类型参数。以 changelog 中的Box为例:
type Box(value) { Box(password: String, value: value) } fn insert(box: Box(a), value: b) -> Box(b) { Box(..box, value:) }这里box的类型是Box(a),而函数返回类型是Box(b)——通过记录更新把value字段从类型a替换为b,成功完成泛型参数的类型变换。
源码印证:类型检查器测试 custom_types.rs 中的generic_record_update1与generic_record_update2两个用例精确验证了这一行为:
generic_record_update1:fn(Box(Int), String) -> Box(String),证明具体类型替换可行;generic_record_update2:fn(Box(a), b) -> Box(b),证明泛型参数之间的替换可行。
同时,changelog 也提到该特性伴随记录更新代码生成的优化(由 yoshi 贡献),生成的目标代码更高效。
2.3 空块语义:等价于todo
现在允许书写没有任何表达式的块(block),例如空函数体:
pub fn main() { // 空函数体 }空块会被编译器视为未完成的代码,就像其中含有一个todo表达式一样,从而触发未完成警告,防止开发者无意中留下空实现。
源码印证:解析器在 parse.rs 中将空块解析为TodoKind::EmptyBlock;类型检查器在 expression.rs 中将TodoKind::EmptyBlock与TodoKind::Keyword、TodoKind::IncompleteUse一并处理;AST 层面空块与空函数、不完整use统一归为Todo类节点(见 typed.rs 与 ast.rs 中的TodoKind::EmptyBlock)。
2.4 按变体标记@deprecated
@deprecated属性此前只能作用于整个类型或函数;1.7.0 起可以单独弃用自定义类型的某个构造变体:
pub type HashAlgorithm { @deprecated("Please upgrade to another algorithm") Md5 Sha224 Sha512 } pub fn hash_password(input: String) -> String { hash(input:, algorithm: Md5) // Warning: Deprecated value used }- 使用被弃用变体会产生"Deprecated value used"警告;
- 可以为每个变体提供不同的弃用说明,指导使用者迁移方向。
源码印证:类型检查器测试 custom_types.rs 覆盖了类型级弃用、变体级弃用、全部变体弃用等场景;对应快照包括 deprecated_varients_type.snap(部分变体弃用)与 deprecated_all_varients_type.snap。与@deprecated相关的警告体系(弃用常量、弃用函数、跨模块调用弃用项)也由 warnings.rs 中的快照测试完整覆盖。
2.5 目标平台简写erl/js弃用
@target(erl)、@target(js)等代码中的目标平台简写名称现在被标记为弃用。这是为后续更正式的目标名称演进做准备,弃用期间编译器会提示,但代码仍可正常工作。迁移时建议关注新版本发布说明中推荐的替代写法。
2.6 卫语句与位数组优化
- 限定记录可用于子句卫语句(clause guards):
case表达式卫语句中现在可以使用Some(record)这类带限定名的记录模式,扩展了卫语句的表达能力。 - JavaScript 目标:位数组字节对齐切片 O(1):在 JavaScript 目标上,对位数组(bit array)进行字节对齐(byte-aligned)切片由 O(N) 降为O(1),显著提升性能。源码中 decision.rs 针对"从字节对齐偏移处读取单个无符号字节"等场景有专门的编译期处理注释与分支。
- JavaScript 目标:Int 位段编译期求值:位数组表达式与模式中的
Int值,在可行时于编译期求值,生成的代码更快且更小。对应实现见 expression.rs 中对字面量与编译期零值/非零值的优化分支。 - rc2 修复:修复了 JavaScript 上尾随
:bytes段导致切片位数组模式匹配结果错误的问题。
2.7 其他编译器改进
- 移除了"当需要
a却使用了Result(a, b)时"关于模式匹配的编译器提示,减少噪音; - 类型构造器被当作值构造器使用时,错误信息更清晰;
- 编译器找不到所需 shell 命令时,输出更易理解的错误信息;
- 浮点字面量以
e结尾且缺少指数部分时,现在会直接报错。
3. 构建工具(Build Tool)改进
3.1gleam add与gleam remove
gleam add:当试图添加不存在的包时,错误信息得到改进,明确指出该包在 Hex 上不存在;gleam remove:不再需要网络连接即可从gleam.toml移除依赖,离线环境下也能操作。
3.2 外部文件允许出现在子目录
此前src/与test/下的外部实现文件(如.mjs、.erl)只能在顶层;1.7.0 起允许出现在src/和test/的子目录中。这使得 FFI 文件可以按模块子目录组织,与 Gleam 模块的目录结构保持一致(仓库测试项目 subdir_ffi 即演示了子目录中的.erl、.mjs、.ex、.hrl文件布局)。
同时修复了一个相关 bug:同名module.mjs不再会被module.gleam静默覆盖,现在会直接报错。
3.3gleam publish安全增强
- 发布 Gleam 团队维护的包以及 v0 版本包时,需要更详细的确认,防止误发布;
- 发布定义多个顶层模块的包时会给出警告:多顶层模块可能导致命名空间污染,给下游使用者带来冲突风险。
3.4 Hex API Key 持久化与加密
与 Hex 包管理器 API 交互的命令(如发布相关操作)不再每次生成新密钥,而是创建并本地存储一个 API key,并使用本地密码加密,降低 Hex 主密码泄露的风险。实现位于 hex/auth.rs:其中定义了LOCAL_PASS_PROMPT("Local password")提示语,并在创建新 API key 时要求用户设置本地密码用于加密(见该文件第 17 行与第 73-75 行的注释与逻辑)。
3.5 新项目依赖与 rebar3 优化
- 新建项目现在要求
gleam_stdlibv0.44.0起; - 使用 rebar3 编译 Erlang 依赖时,构建工具会设置
REBAR_SKIP_PROJECT_PLUGINS环境变量。未来版本的 rebar3 将据此跳过项目插件,从而显著减少需要下载和编译的代码量,缩短编译时间。
4. 语言服务器(Language Server)新代码动作
1.7.0 为 LSP 添加了大量高价值的代码动作(code action),以下是全部新动作及 changelog 中的示例。这些动作的注册与实现可在 code_action.rs 与 engine.rs 中溯源,测试集中在 action.rs。
4.1use表达式与函数调用的互转
动作一:将use表达式脱糖(desugar)为等价函数调用。例如:
pub fn main() { use profile <- result.try(fetch_profile(user)) render_welcome(user, profile) }转换为:
pub fn main() { result.try(fetch_profile(user), fn(profile) { render_welcome(user, profile) }) }动作二:反向操作,将函数调用转为use表达式。例如:
pub fn main() { result.try(fetch_profile(user) fn(profile) { render_welcome(user, profile) }) }转换为:
pub fn main() { use profile <- result.try(fetch_profile(user)) render_welcome(user, profile) }rc3 修复:这两个动作此前在涉及**带标签参数(labelled arguments)**时可能生成非法代码,已在 rc3 修复。实现层面,code_action.rs 中明确处理了"被脱糖的 use 回调使用标签"的情况。
4.2 非穷尽let转case
当let赋值模式非穷尽时,语言服务器现在建议将其转换为等价的case表达式:
pub fn unwrap_result(result: Result(a, b)) -> a { let Ok(inner) = result inner }转换为:
pub fn unwrap_result(result: Result(a, b)) -> a { let inner = case result { Ok(inner) -> inner Error(_) -> todo } inner }转换后缺失的分支会以todo占位,编译器会继续对未处理分支给出"非穷尽"警告,引导开发者补全。源码印证:该动作由 code_action_inexhaustive_let_to_case 实现,它遍历类型检查阶段报告的InexhaustiveLetAssignment错误来定位需要转换的赋值;测试覆盖了元组、位数组、字符串前缀、别名、嵌套等多种模式场景(见 action.rs)。
4.3 提取值为变量
新增"提取值为变量"动作,可把表达式提取为具名变量。注意实现中对块(block)等部分场景做了限制(见 code_action.rs 附近注释)。
4.4 展开函数捕获(Function Capture)
将函数捕获(partial application)展开为等价的匿名函数。例如:
pub fn main() { list.map([1, 2, 3], int.add(_, 11)) }转换为:
pub fn main() { list.map([1, 2, 3], fn(value) { int.add(value, 11) }) }实现由ExpandFunctionCapture构建器完成,见 code_action.rs。
4.5 生成 Dynamic Decoder
为自定义类型一键生成gleam/dynamic/decode的解码器。例如对于:
pub type Person { Person(name: String, age: Int) }生成:
import gleam/dynamic/decode pub type Person { Person(name: String, age: Int) } fn person_decoder() -> decode.Decoder(Person) { use name <- decode.field("name", decode.string) use age <- decode.field("age", decode.int) decode.success(Person(name:, age:)) }源码印证:该动作由GenerateDynamicDecoder构建器实现(见 code_action.rs),快照测试覆盖了多变体类型、无字段变体、泛型类型、外部包类型(不为外部包类型生成零值)等边界情况,例如 generate_dynamic_decoder_complex_types.snap 与 generate_dynamic_decoder_for_multi_variant_type.snap。
4.6 悬停与补全改进
- 参数标签悬停:在参数标签(argument labels)上悬停时提供类型信息;
use表达式中的模式悬停:对use表达式中的模式提供正确的信息;- 类型导入补全修复:修复了本应包含花括号的类型导入补全缺少花括号的问题。
5. 格式化器(Formatter)与文档
5.1 格式化器变更
- 函数捕获格式化为普通函数调用形式(rc3);
- 记录更新格式化为函数调用形式(rc3);
- 空块自动补
todo:格式化器会在空块中插入todo,与编译器"空块视为未完成"的语义保持一致; - 常量中的列表与表达式中的列表格式统一。
5.2 文档生成:Canonical Links
文档发布到 Hex 时,现在会为文档页面生成canonical link,将带版本号的页面解析并指向最新版页面,帮助搜索引擎正确归并版本化内容。此前已发布的文档需要手动更新,1.7.0 起自动生成。
6. Bug 修复汇总
| 领域 | 修复内容 |
|---|---|
| 类型系统 | 私有类型不再被允许在其他模块中使用(rc3);推断变体缓存修复,消除增量构建与 LSP 中的错误报错;prelude 类型常量的变体推断修复 |
| 模式匹配 | 多主题匹配中常量为记录时的崩溃修复;use表达式中的模式现在会检查穷尽性;嵌套元组访问左端为函数调用时解析错误修复 |
| 代码生成 | 旧版 Erlang/OTP 上 BEAM 字节码编译失败修复;项目路径含非 ASCII 字符时无法编译为 BEAM 的修复;JavaScript 字符串模式前缀变量嵌套赋值产生非法代码的修复;引用外部函数时不安全整数警告缺失的修复 |
| 增量编译 | 依赖被删除或重命名的模块现在会自动重新编译 |
| 警告 | 未使用的case表达式、代码块与管道(可安全移除)现在会给出警告;特定场景下"忽略未使用变量"提示错误修复 |
| 构建/文件系统 | NTFS 分区上 ENOTEMPTY(os error 39)构建崩溃修复(rc3);gleam new生成的 GitHub test workflow 不再使用不兼容的 Erlang OTP 与 Elixir 版本组合 |
| 依赖管理 | 修复gleam update无法更新版本的问题(该修复记录在 v1.6.1 小节中,随 1.7.0 一并生效) |
7. 升级建议与验证方式
- 升级路径:若使用旧版 Gleam,可通过官方安装方式升级到 1.7.0;新项目将自动采用
gleam_stdlibv0.44.0 及以上的模板。 - 关注弃用:检查代码中
@target(erl)/@target(js)的使用,为后续移除做准备;使用@deprecated变体时留意编译器警告并规划迁移。 - 体验新能力:在支持 LSP 的编辑器(VSCode、Neovim 等)中直接尝试第 4 节的五个新代码动作——特别是"生成 Dynamic Decoder"与"非穷尽
let转case",能显著减少样板代码。 - 回归验证:本仓库的测试矩阵覆盖了上述全部特性,可参照 erlang/tests、javascript/tests 与 language-server/src/tests 中的用例,使用
cargo test对相应模块运行测试验证本地构建行为。
本文内容基于当前仓库中 changelog/v1.7.md 的官方发布记录整理,并结合仓库源码与测试用例进行印证;特性行为以你所使用的具体 Gleam 版本为准。
【免费下载链接】gleam⭐️ A friendly language for building type-safe, scalable systems!项目地址: https://gitcode.com/GitHub_Trending/gl/gleam
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考