Erlang 从零 · 第 22 课(展开目录)
01 · 先打开 erl02 · 先认常见 term03 · 分清两种文字04 · 变量只接一次05 · 拆开一列,再筛一次06 · 把文字安全变成整数07 · 用 case 选一条路08 · 让函数继续走09 · 把代码放进模块10 · 条件必须说真话11 · 一层一层取数据12 · 文字先说清编码13 · 让函数子句排好队14 · 带着累加器往前走15 · 先看值,再选路16 · 给模块立规矩17 · 让 rebar3 管项目18 · 让结果有固定形状19 · 把磁盘当成会失败的邻居20 · 把函数交给 list21 · record 留在院里22 · 先写模块之间的约定23 · 把 term 形状写出来24 · 小测试与整段路25 · 把应用磨成命令26 · 先把小项目圈住27 · 把真实文件洗干净28 · 公开入口越少越稳29 · 把约定留在源码旁30 · 打包,也留下线索31 · 按最初任务交卷
ERLANG · 进阶 · LESSON 2240 分钟
先写模块之间的约定
用 behaviour callback 定义接口,再写可替换实现。
01 · 先看完整例子
分别保存为 greeter.erl 与 zh_greeter.erl
编译两个模块后运行 zh_greeter:hello(<<"小满"/utf8>>).。
Erlang
%% greeter.erl
-module(greeter).
-callback hello(binary()) -> binary().
%% zh_greeter.erl
-module(zh_greeter).
-behaviour(greeter).
-export([hello/1]).
%% 实现 behaviour 要求的 hello/1
hello(Name) ->
<<"你好,"/utf8, Name/binary>>.先对照结果
- 得到包含
你好,小满的 UTF-8 binary。
02 · 回头拆代码
从第一行往下读
- behaviour 模块只写约定,不提供这次的具体问候。
- 实现模块声明 behaviour 并导出 callback。
- 缺少
hello/1时,编译器会给出警告。
03 · 认清新符号
符号不是暗号
-callback声明回调函数和类型。
-behaviour(Module).声明当前模块遵守该 behaviour。
Module:Function(Args)通过传入模块调用具体实现。
04 · 代码里的概念
把名字和意思对上
behaviour
一组模块必须实现的函数约定。
callback
behaviour 声明、实现模块负责提供的函数。
05 · 再说清楚
这些写法为什么有用
behaviour 说明一个模块应提供哪些函数。实现模块可以不同,调用者只依赖共同约定。
-callback 声明 name/arity 及类型。实现模块写 -behaviour(Name).,编译器会提醒缺失 callback。
behaviour 常用在 OTP,也适合存储、通知等项目边界。
设计选择
为什么这样写
为什么 behaviour 只规定回调,不直接提供进程?
behaviour 先约定模块必须实现哪些函数。GenServer/gen_server 是带运行时协议的 behaviour,但一般 behaviour 本身不会启动任何东西。
它统一边界,却不替你决定状态、监督、容量和错误策略。
换一把尺子
从 Java、Python、JavaScript 看过来
Java
- 熟悉的起点
- interface 定义方法契约。
- BEAM 在意什么
- behaviour 约束 module/function/arity;调用者仍调用模块函数。
- 别带错直觉
- 实现 behaviour 不等于创建对象或线程。
Python
- 熟悉的起点
- ABC 或 Protocol。
- BEAM 在意什么
- callback 元数据让工具检查实现,也能被 OTP 框架调用。
- 别带错直觉
- 它不是 Python 装饰器式的运行时包装。
JavaScript
- 熟悉的起点
- 约定对象形状或 TypeScript interface。
- BEAM 在意什么
- behaviour 存在于 BEAM 模块与 callback 入口层。
- 别带错直觉
- 类型约定和进程生命周期仍是两件事。
06 · 自己改一次
合上答案,动手
增加 quiet_greeter,忽略名字并返回 <<"hello">>。
练习起点
-module(quiet_greeter).
-behaviour(____).
-export([hello/1]).
hello(_Name) ->
____.目标结果: 模块遵守 greeter,调用返回 <<"hello">>。
卡住了,再看提示
behaviour 名是 greeter,返回值是 binary。
运行过以后,再看一种答案
一种答案
-module(quiet_greeter).
-behaviour(greeter).
-export([hello/1]).
%% 这个实现选择忽略名字
hello(_Name) ->
<<"hello">>.想一想:behaviour 会自动生成 callback 正文吗?
不会。它提供约定和编译检查,具体函数仍由实现模块编写。
带走
记住三句
- 1
behaviour 约束模块接口。
- 2
callback 写清 name/arity 与类型。
- 3
依赖约定可替换具体实现。
本课结束代码也跑过一次,就把这一课收好。