OTP and Gleam
摘要
Gleam语言创造者Louie深入解释了OTP的核心概念,以及Gleam如何通过类型安全的方式与OTP协同工作,实现了与Erlang/Elixir完全兼容的进程间通信和监督者等构建块。
<p><a href="https://lobste.rs/s/vhp5x9/otp_gleam">Comments</a></p>
查看缓存全文
缓存时间: 2026/06/11 13:39
**TL;DR:** 演讲者 Louie(Gleam 创造者)深入解释了 OTP 是什么、Gleam 如何与 OTP 协同工作,以及如何通过类型安全的方式实现进程间通信,最终证明 Gleam 能完全发挥 OTP 的魔法,同时保留静态类型的好处。
## 引言
大家好,非常感谢。今天真是激动人心的一天——我们有了一整天的 Gleam 演讲,整个 Gleam 轨道。这个演讲叫做“OTP 与 Gleam”,我想展示这两个重要的东西如何协同工作,并解释它们能不能协同、有哪些问题和挑战,还要深入内部看看为什么决策是那样做出的,因为这种背景能帮助你更好地理解系统。
我是 Louie,我创造了 Gleam,所以他们让我来做这个演讲。
## 什么是 OTP?
OTP 这个名称(开放电信平台)本身没什么意义,刚开始接触 BEAM 时很难准确理解它究竟是什么。我认为要最好地理解它,需要稍微后退一步,把 BEAM 看作一个整体。
BEAM 给了我们(也喜欢吹嘘的)特性:大规模可扩展性、无与伦比的可靠性、生产环境可观测性、可靠的低延迟能力以及分布式计算。但仔细想想,BEAM 并没有直接给我们这些特性——它给了我们**消息**,给了我们**错误隔离**。你可以看到这些事情之间的关系,但仅仅拥有一个原语,并不一定意味着你拥有另一个特性。
我们的工作是获取这些原语、这些有用的工具,并将它们转化为一个拥有我们作为 BEAM 开发者想要吹嘘的特性的系统。如果只是从这些小构建块开始尝试构建,很容易犯错,尤其是处理并发之类的问题。
所以,我认为这才是 OTP 的真正含义:OTP 是一堆代码,随你的 Erlang 发行版一起提供,它利用那些 BEAM 原语,实现了一堆非常有用的设计模式,为我们提供了可以在程序中使用的一系列构建块:监督者、通用服务器等等。这些实现是由杰出的人构建的,并且经过了近 30 年的彻底测试和使用,我们知道可以信赖它们。
OTP 的最后一个部分,是一套协议、一套接口。实现这些接口的东西可以插入到更大的 OTP 系统中,比如响应系统消息的特殊进程、为某种模块行为实现回调的模块等等。这就是我对 OTP 的理解。**OTP 是 BEAM 魔法的关键**——当我们喜欢吹嘘 BEAM 的特性时,实际上可能是在吹嘘 OTP。无论你写 Erlang 还是 Elixir,这都是事实。
## 问题:Gleam 能用 OTP 吗?
有人会在论坛上说:“哦,你不能在 Gleam 中使用 OTP,因为有类型。”而类型在本质上对 actor 之类的东西是不好的?这不可能行得通。但事实上,**它完全行得通**。我们需要让人们非常清楚,这些东西可以出色地协同工作。我们不会完全按照 Erlang 和 Elixir 的方式来做,因为它是另一门语言,但它就在那里,我们可以使用相同的框架。
## OTP 在 Gleam 中的实现方式
OTP 在 Gleam 中以两个包的形式实现,它不是语言的一部分(类似 Erlang 和 Elixir)。在 Gleam 中分离还有一个额外原因:我们还需要在 JavaScript 上运行,而 JavaScript 没有那些 BEAM 原语,无法在其上构建 OTP。
这两个包的目标:
- **生产就绪且高效**:我们希望人们做出真实的东西,而不是学术项目。
- **与 Erlang OTP 完全兼容**:我们不制造独立的 Gleam OTP,它是 Gleam 进入现有 OTP 的窗口。Gleam 程序可以借助所有前辈 BEAM 开发者创造的好东西,反过来,用 Gleam 做的东西,Elixir 的朋友们也能利用。
- **提供核心构建块**:监督者、通用服务器等。
- **静态类型安全**:在涉及并发和 actor 时,类型系统魔法不应消失。
- **富有表现力和能力**:用 Erlang 能写的程序,用 Gleam 也应该能写。不是做一个严格更弱的东西,而是等价的。
- **长期可持续**:OTP 快 30 岁了,Gleam 也应当像虚拟机一样持续到永远。
## 如何实现类型安全?两种方法
### 方法一:重用现有 API(以监督者为例)
以静态子进程创建监督者为例,Gleam API 与 Erlang 的工作方式非常相似,只是省略了子进程规范列表,使用了管道语法。语义完全相同,我们可以用其中一个实现另一个:导入 `start_link` 函数(私有),然后放一个漂亮的包装器,将 Gleam 期望的数据结构转换为 Erlang 期望的形状。**Erlang 已经很好了,我们不必重新发明轮子**,如果可行就使用它。
### 方法二:重新设计 API(以通用服务器为例)
有时 API 在某种程度上不适合 Gleam,不能轻易用小函数掩盖。例如 `handle_call` 回调:在 Erlang 中一个 case 表达式可以返回整数或布尔值,但 Gleam 不允许 case 分支返回不同类型的值,也没有整数和布尔值的联合类型。所以必须设计不同的 API。
我们必须像 Erlang 那样,在同样的原语之上用 Gleam 实现 Gleam OTP。核心原语有六个:`spawn`、`link`、`trap_exit`、`monitor`、`send` 和 `receive`。前四个很容易导入或加包装。
**最大的问题是 `send` 和 `receive`**:如果直接导入 Erlang 的设计,接收者和消息类型之间没有关系,任何进程可以向任何进程发送任何类型消息,这不是我们追求的类型安全。我们希望只能向接受它们的进程发送消息。
一种常见尝试是参数化 PID:带上消息类型参数(如 `PID(Int)`),但这不够灵活——同一个 PID 需要能接收不同类型的消息(如温度和是否下雨的回复),但回复类型不同,PID 无法编码这些语义。
实际上,Erlang 已经解决了这个问题。查看 `gen` 模块内部可以发现 `from` 数据结构,它将语义标识信息附加到进程收件箱中的单个消息上,用于匹配“这是对什么的回复”。我们可以采用同样的设计,忠实地转换成 Gleam,加一个类型参数(消息类型),并把 `from` 重命名为 `subject`,`reply` 重命名为 `send`,概念已经存在。这样我们就有了类型化消息发送的方式。
对于同时从多个不同 subject 接收,可以使用 `selector`,例如接收字符串消息和整数消息,然后把整数映射成字符串。
所有原理都有了,这足够实现 gen_server 吗?gen_server 做几件事:同步初始化(成功或失败并传回值给父进程)、持有并更新状态、处理系统消息(用于 observer 调试)、处理同步和异步消息(主要业务逻辑)。**我们有和 Erlang 相同的六个原语,可以直接复制 Erlang 的实现**——打开 Erlang 源码,直接翻译过来,只做必要的类型适配。
## 结论
Gleam 能够完全使用 OTP 的魔法,同时保持静态类型安全。通过巧妙的设计(如 subject/selector),我们既获得了类型系统的好处,又没有牺牲表达能力。Gleam OTP 与 Erlang OTP 完全兼容,而且能在相同的时间尺度上持续发展。
**Source:** https://www.youtube.com/watch?v=DJwxAJoUUOU
相似文章
Gleam 已将其源代码镜像到 tangled(一个基于 AT 协议的代码托管平台)
Gleam 是一种类型安全的编程语言,已将其源代码镜像到 tangled,这是一个基于 AT 协议构建的代码托管平台。
Reasons and Resources for Learning The Gleam Programming Language
介绍学习 Gleam 编程语言的五大理由(跨平台、类型安全、简洁设计、函数式范式、活跃生态)以及从官网导览到 Exercism 等实用入门资源。
Gleam and the value of small
Gleam 是一门刻意保持简洁的函数式编程语言,通过精简关键字、避免重复来提升可读性和开发体验,其设计理念是“少即是多”。
Gleam Gathering 2026 Lighting Talks
Gleam Gathering 2026 的闪电演讲涵盖了开放遥测封装、Luster HTML 与 SPA 对比、收据打印机 DSL、本机后端的幽默构建过程以及 Minecraft 数据生成,展示了 Gleam 生态中的实用工具和实验性项目。
Gleam v1.17.0
Gleam v1.17.0 引入了 `gleam export escript` 命令以创建单文件 BEAM 程序,在语言服务器中高亮引用,以及常量 `todo` 表达式。此外,首届 Gleam Gathering 大会的视频也已发布。