Gleam v1.17 - Single file Gleam BEAM programs with escript

Gleam v1.17 - Single file Gleam BEAM programs with escript

Gleam is a type safe and scalable language for the Erlang virtual machine and JavaScript runtimes. Today Gleam v1.17.0 has been published.

Gleam 是一门为 Erlang 虚拟机和 JavaScript 运行时设计的类型安全且可扩展的语言。今天,Gleam v1.17.0 已正式发布。

Gleam Gathering

But first: the first videos from the first ever all-Gleam conference have been released! You can view them on the Gleam Gathering YouTube account. The event was a roaring success and loads of fun. Stay tuned for news of the next one in 2027!

Gleam Gathering

首先要宣布的是:首届 Gleam 全球大会的视频已经发布了!你可以在 Gleam Gathering 的 YouTube 账号上观看。这次活动取得了巨大的成功,非常有趣。请持续关注 2027 年下一届大会的消息!

BEAM escripts

When running on the Erlang virtual machine Gleam code is compiled to a series of .beam files, each of which contains the bytecode for a single Gleam module. This works fine for programs distributed or installed using package managers, containers, or other such systems, but having many files to share is a little inconvenient for sharing small command-line programs.

BEAM escripts

当在 Erlang 虚拟机上运行时,Gleam 代码会被编译成一系列 .beam 文件,每个文件包含一个 Gleam 模块的字节码。对于通过包管理器、容器或其他系统分发或安装的程序来说,这运行良好;但对于分享小型命令行程序而言,需要分发多个文件就显得有些不便。

In the JavaScript world this problem is solved using a “bundler”, a program that takes many JavaScript modules and combines them into a single file. This single file can be copied to and run on any computer that has a JavaScript runtime installed (such as NodeJS, Deno, or Bun). Erlang has a similar solution: escripts. Much like a JavaScript bundle, an escript is a single file that contains all the modules of a program in the form of pre-compiled bytecode, and it can be run on any computer that has Erlang installed.

在 JavaScript 世界中,这个问题通过“打包器(bundler)”来解决,它将多个 JavaScript 模块合并为一个文件。这个单一文件可以复制到任何安装了 JavaScript 运行时(如 NodeJS、Deno 或 Bun)的计算机上运行。Erlang 也有类似的解决方案:escripts。就像 JavaScript 的 bundle 一样,escript 是一个包含程序所有模块预编译字节码的单一文件,可以在任何安装了 Erlang 的计算机上运行。

The Erlang build tool has a convenient command for creating an escript, but for Gleam programmers the escript creation process has not been as straightforward. This release brings the gleam export escript command, which will compile the project, verify it has a valid main function, and build the escript file from the compiled bytecode.

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!

Highlight references

Gleam’s language server provides IDE functionality for all editors that implement the language server protocol. This release adds support for the textDocument/documentHighlight feature, highlighting all references to a selected variable.

高亮引用

Gleam 的语言服务器为所有实现语言服务器协议(LSP)的编辑器提供了 IDE 功能。本次发布增加了对 textDocument/documentHighlight 特性的支持,可以高亮显示所选变量的所有引用。

For example, in this code triggering it with the cursor over any instance of vec will result in these highlights:

例如,在以下代码中,将光标置于 vec 的任何实例上触发该功能,将产生如下高亮效果:

fn to_cartesian(vec) { // ^^^
  let x = vec.rho * cos(vec.theta) // ^^^ ^^^
  let y = vec.rho * sin(vec.theta) // ^^^ ^^^
  #(x, y)
}

Thank you Gavin Morrow for this addition!

感谢 Gavin Morrow 带来的这一改进!

Constant todo expressions

Gleam’s todo keyword is a placeholder expression that programmers can use when they have not-yet-finished code they wish to type-check or run. At compile time it outputs a warning to say that the code is incomplete, and if the code path with the todo expression is run then it will panic, exiting the program.

常量 todo 表达式

Gleam 的 todo 关键字是一个占位符表达式,程序员可以在代码尚未完成但希望进行类型检查或运行时使用它。在编译时,它会输出警告提示代码不完整;如果运行到包含 todo 表达式的代码路径,程序将会 panic 并退出。

todo can now also be used in constant expressions. Since constant expressions are evaluated at compile time, when todo is used in a constant the program can no longer be run, but it can still be type checked and analysed. This has also enabled us to upgrade the “Fill labels” code action so that it works with constants too. When run it fills in the missing labelled arguments from a record constructor.

现在 todo 也可以用于常量表达式中。由于常量表达式是在编译时求值的,当在常量中使用 todo 时,程序虽然无法运行,但仍然可以进行类型检查和分析。这也使我们能够升级“填充标签(Fill labels)”代码操作,使其同样适用于常量。执行该操作时,它会自动补全记录构造函数中缺失的标签参数。

For example: 例如:

pub type Pokemon { Pokemon(number: Int, name: String, hp: Int) }
pub const cleffa = Pokemon(number: 173)

In this code snippet we haven’t specified the name and hp fields, that’s an error! Triggering the “Fill labels” code action will result in the following:

在这个代码片段中,我们没有指定 namehp 字段,这会报错!触发“填充标签”代码操作后,结果如下:

pub const cleffa = Pokemon(number: 173, name: todo, hp: todo)

Thank you Giacomo Cavalieri!

感谢 Giacomo Cavalieri!

Record update hovering

Hovering in your editor is a great way to gleam more information, with the language server showing the types, documentation, and other details of whatever is being hovered over. Gleam’s record update syntax is used to create a new record from an existing one, but with some fields updated with new values. When hovering over one of these the language server will now also show the remaining fields that have not been given new values, saving you from navigating to the definition to see what others you could set new values for.

记录更新悬停提示

在编辑器中悬停是获取更多信息的绝佳方式,语言服务器会显示悬停内容的类型、文档和其他详细信息。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)
  // ^^^^^ Hovering this will show:
  // Unchanged fields:
  // - name
}

Thank you Giacomo Cavalieri!

感谢 Giacomo Cavalieri!

Unknown value import suggestions

In Gleam functions from other modules are almost always used in a qualified fashion, written as dict.fold rather than just fold. This is done so it is clearer to the reader where the function is defined and what it does, and to prevent redundant suffixes being added to function names to indicate what type they work with.

未知值导入建议

在 Gleam 中,来自其他模块的函数几乎总是以限定方式使用,写作 dict.fold 而非仅仅是 fold。这样做是为了让读者更清楚函数定义的位置及其功能,并防止为了表明函数适用的类型而给函数名添加冗余后缀。

Sometimes the programmer may forget to write the module qualifier, resulting in a compile error due to there being no value with that name in scope. When this happens the compiler will now search in the imported modules for an appropriate value with that name, and suggest it as a position correction.

有时程序员可能会忘记写模块限定符,导致因作用域内没有该名称的值而产生编译错误。当这种情况发生时,编译器现在会在已导入的模块中搜索该名称的合适值,并将其作为修正建议提供。

For example, for this invalid program: 例如,对于这个无效程序:

import gleam/io
pub fn main() -> Nil {
  println("Hello, World!")
}

The compiler will display this error message: 编译器将显示以下错误信息:

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

Thank you raphrous!

感谢 raphrous!

Context aware type printing in warnings

Up until now when the name of a type is displayed in a warning it has been displayed using its canonical name, but this may not be how the programmer has referred to the type in the code. A type could be referred to in a qualified way such as accounting.Invoice, or the programmer could have aliased the type to a new name to make it easier to understand in the context of that code. Types in warnings are now correctly qualified or aliased when displayed in warnings.

警告中上下文感知的类型打印

在此之前,当类型名称在警告中显示时,它使用的是其规范名称,但这可能并非程序员在代码中引用该类型的方式。类型可能以限定方式引用(如 accounting.Invoice),或者程序员可能已将该类型别名为新名称,以便在代码上下文中更易于理解。现在,警告中的类型在显示时会正确地使用限定名或别名。