模块 10查看课程目录
Erlang ↔ Elixir 互操作
共享 VM 很容易;设计清楚边界,仍然需要刻意。
先修正心智模型,再增加 API
同一 VM 内互调几乎没有仪式感,这很强大,也容易让边界失控。最常见的坑不是函数找不到,而是字符串表示、record 形状、struct 假设和不同异常约定。
可验证的学习目标
- 能双向调用模块并处理 module atom
- 能显式转换 binary、String 与 charlist
- 能为跨语言异常和返回值建立稳定协议
- 完成共享语义
- 会用 Mix 与 Rebar3
先抓住三个词
charlist
整数 code point 的 list,Erlang 旧 API 中常见;Elixir 中用 `~c"text"` 表示。
record
Erlang 编译期 tuple 语法糖。跨语言直接依赖 tuple 位置很脆弱,优先使用公开访问函数或 map 协议。
exception boundary
一边抛出 exception/exit/throw 时,另一边如何捕获并转为稳定错误 tuple 的约定。
先对齐协议,再看标点
转换发生在边界,核心协议使用带标签 tuple。不要让 charlist 假设渗透整个 Elixir 代码库。
defmodule Scheduler do
@spec submit(binary(), map()) ::
{:ok, reference()} | {:error, atom()}
def submit(queue, payload) when is_binary(queue) do
:job_worker.submit(
String.to_charlist(queue),
Map.to_list(payload)
)
catch
:exit, reason -> {:error, normalize_exit(reason)}
end
end-module(job_worker).
-export([submit/2, call_elixir/1]).
submit(Queue, Payload)
when is_list(Queue), is_list(Payload) ->
{ok, make_ref()}.
call_elixir(Value) ->
'Elixir.Scheduler':normalize(Value).把字符串陷阱变成测试
让 Erlang worker 只接受 charlist;先直接传 Elixir String 触发错误,再在边界转换并固定测试。
- 01
从 Elixir 传入 `"jobs"`,记录 guard 或 function clause 错误
- 02
改用 `String.to_charlist/1`
- 03
为中文队列名加入 round-trip 测试
mix test test/interoperability_test.exs- binary 与 charlist 匹配不同 guard
- 显式转换后协议通过
- Unicode 测试防止按字节错误处理
从 Erlang 返回一个内部 record tuple,让 Elixir 按位置取值;随后给 record 增加字段,观察脆弱耦合。
显式边界转换可让两边对字符串 term 达成一致。
一个成功转换不证明第三方库所有返回路径都遵守同一字符串约定。
跨语言调用中,最稳妥的长期返回协议通常是什么?
混合语言 worker
Elixir 提供公共 API 与验证,Erlang gen_server 承担队列状态;两边各自写测试,并互相穿过边界。
提示 1轻推一下
先写 term 协议文档。
提示 2缩小问题
所有转换集中在 adapter 模块。
提示 3接近实现
让 ExUnit 覆盖 Erlang worker,EUnit 覆盖 Elixir normalize 函数。
提示 4用验收标准反推
从下面每一条验收标准倒推一个最小测试。若某条无法写成测试,先把表述改成可观察结果。
- 两种语言都承担真实职责
- binary/charlist 和异常转换集中在边界
- 双向测试能发现协议漂移
复习卡
- 1共享 VM 让调用简单,稳定协议让维护简单。
- 2字符串、record 与异常是最常见摩擦面。
- 3adapter 应该薄、集中、双向可测试。