Erlang 从零 · 第 16 课(展开目录)
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 1640 分钟

给模块立规矩

用模块属性声明身份、导出、常量和 record 字段。

01 · 先看完整例子

保存为 library.erl

编译后运行 B = library:new(<<"Small Town">>), library:info(B).。

Erlang
-module(library).
-export([new/1, info/1]).

-define(DEFAULT_PAGES, 1).
-record(book, {title, pages = ?DEFAULT_PAGES}).

%% record 只在知道定义的模块中展开
new(Title) ->
  #book{title = Title}.

info(Book) ->
  {Book#book.title, Book#book.pages}.
先对照结果
  • 得到 {<<"Small Town">>,1}。
02 · 回头拆代码

从第一行往下读

  1. 宏给默认页数一个名字,编译时会展开。
  2. record 字段让代码不用记 tuple 的位置。
  3. 公开 info/1 避免调用者直接依赖 record 内部布局。
03 · 认清新符号

符号不是暗号

-define(NAME, Value).

声明宏,使用时写 ?NAME。

-record(name, {...}).

声明 record 字段与默认值。

#book{...}

创建、匹配或更新 book record。

04 · 代码里的概念

把名字和意思对上

01

模块属性

告诉编译器模块身份、公开函数、类型等信息的声明。

02

record

为固定 tuple 位置提供字段名的编译期语法。

05 · 再说清楚

这些写法为什么有用

Erlang 模块属性以连字符开头,编译时生效。-module、-export 都是属性。

-define 声明宏常量。record 为 tuple 字段提供名字,但它主要是编译期语法,不是全局运行时类型。

record 定义通常放模块或头文件中。跨模块边界时,map 往往更容易演进。

06 · 自己改一次

合上答案,动手

增加 set_pages/2,返回页数更新后的 record。

练习起点
set_pages(Book, Pages) ->
  Book#book{____ = ____}.

目标结果: 传入 120 时,新 record 的页数为 120。

卡住了,再看提示

字段名是 pages,新值变量是 Pages。

运行过以后,再看一种答案
一种答案
%% record 更新也返回新 term
set_pages(Book, Pages) ->
  Book#book{pages = Pages}.
想一想:record 是否像 map 一样在运行时保存字段名?

不是。record 主要在编译时展开成 tuple;运行时值本身不携带这些字段名。

带走

记住三句

  1. 1

    模块属性服务编译器。

  2. 2

    record 给固定 tuple 布局加字段名。

  3. 3

    对外 API 不应轻易暴露 record 布局。

本课结束代码也跑过一次,就把这一课收好。