模块 04查看课程目录
两种语法,一套语义
把语法糖剥开,建立一张长期可用的互译表。
先修正心智模型,再增加 API
如果把两门语言分别背一遍,你会重复学习并在互操作时迷失。更高效的方法是只讲一次 BEAM 语义,再明确语法映射与少数真实差异:协议/behaviour 表达、字符串约定、异常语法、工具链。
可验证的学习目标
- 能在常见数据、函数和模块调用之间双向翻译
- 能区分 Elixir 语法便利与 BEAM 底层语义
- 能用 spec 与测试固定跨语言协议
- 完成 Elixir 与 Erlang 基础
- 会写纯函数测试
先抓住三个词
atom 映射
Elixir 的 `:ok` 与 Erlang 的 `ok` 是同一个 atom;Elixir 模块名本身也是带 `Elixir.` 前缀的 atom。
模块调用
`Foo.bar()`、`:lists.reverse/1` 与 `foo:bar()` 最终都指向“模块 atom + 函数 + 元数”。
协议边界
跨模块、跨语言传递的数据形状与错误返回。稳定协议比选择哪种表面语法更重要。
先对齐协议,再看标点
数据协议完全一致:输入两个 atom,输出带标签的 tuple。两边测试甚至可以共享 fixture。
defmodule Order do
@type state :: :new | :paid | :shipped
@spec transition(state(), atom()) ::
{:ok, state()} | {:error, :invalid_transition}
def transition(:new, :pay), do: {:ok, :paid}
def transition(:paid, :ship), do: {:ok, :shipped}
def transition(_, _), do: {:error, :invalid_transition}
end-module(order).
-export([transition/2]).
-type state() :: new | paid | shipped.
-spec transition(state(), atom()) ->
{ok, state()} | {error, invalid_transition}.
transition(new, pay) -> {ok, paid};
transition(paid, ship) -> {ok, shipped};
transition(_, _) -> {error, invalid_transition}.证明 term 能穿过语言边界
从 Elixir 调 Erlang 模块,再从 Erlang 调 Elixir 模块。比较返回值的 inspect 输出。
- 01
在 Mix 项目的 `src/` 放入 `order.erl` 并编译
- 02
在 IEx 调用 `:order.transition(:new, :pay)`
- 03
在 Erlang 代码调用 `'Elixir.Order':transition(new, pay)`
iex -S mix- 两边都得到 `{ok, paid}` 对应的同一个 tuple term
- Elixir 模块可通过其底层 atom 从 Erlang 访问
让一边返回字符串 `"paid"`,另一边仍匹配 atom `paid`。观察接口在语法相似时仍会因 term 不同而失败。
普通 BEAM term 可在两种语言模块之间直接传递。
term 可传递不等于所有异常、struct、record 或字符串约定都能无摩擦互用。
Elixir 的 `Foo` 模块在 Erlang 中通常如何引用?
订单状态机双实现
扩展取消、退款与重复事件,分别写 Elixir/Erlang 纯函数实现,并用同一张状态转换表验证。
提示 1轻推一下
先写状态 × 事件表,再写代码。
提示 2缩小问题
对重复事件决定幂等还是错误,并写进协议。
提示 3接近实现
不要用进程隐藏不清楚的状态转换。
提示 4用验收标准反推
从下面每一条验收标准倒推一个最小测试。若某条无法写成测试,先把表述改成可观察结果。
- 两版实现返回相同 term 形状
- 非法转换有明确错误标签
- spec 与测试覆盖所有状态边
复习卡
- 1先固定 term 协议,再选择语言表达。
- 2Elixir 模块名和 alias 最终仍落到 module atom。
- 3真正危险的边界常在 string/charlist、struct/record 与异常约定。