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

record 留在院里

模块内部使用 record,对外用 map,避免 tuple 布局泄漏。

01 · 先看完整例子

保存为 user_profile.erl

调用者只依赖 map 与结果 tuple,不依赖 record 的 tuple 位置。

Erlang
-module(user_profile).
-export([from_map/1, public/1]).

-record(user, {name, score = 0}).

%% map 是进入模块的边界形状
from_map(#{name := Name, score := Score}) ->
  {ok, #user{name = Name, score = Score}};
from_map(_) ->
  {error, invalid_user}.

%% record 不直接离开模块
public(User) ->
  #{name => User#user.name, score => User#user.score}.
先对照结果
  • 有效 map 返回 {ok, Record};public/1 再返回具名 map。
02 · 回头拆代码

从第一行往下读

  1. 入口模式要求两个键存在。
  2. 内部 record 提供字段访问语法。
  3. 出口函数选择真正允许公开的字段。
03 · 认清新符号

符号不是暗号

#user{}

模块内部的固定 record 表示。

#{name => ...}

边界处带字段名的 map。

from_map / to_map

把边界数据与内部表示相互转换。

04 · 代码里的概念

把名字和意思对上

01

模块边界

一个模块与外部调用者交换数据的位置。

02

表示泄漏

调用者被迫知道本应属于模块内部的数据布局。

05 · 再说清楚

这些写法为什么有用

record 在模块内部很好读,但运行时只是 tuple。把它直接交给远处模块,双方会被同一布局绑住。

边界用 map 传具名字段,内部可用 record 保持固定结构。转换函数就是明确的门。

这不是说 record 不好,而是让数据表示待在最合适的范围。

06 · 自己改一次

合上答案,动手

让 public/1 只公开名字,不公开分数。

练习起点
public(User) ->
  #{____ => User#user.____}.

目标结果: 得到只含 name 键的 map。

卡住了,再看提示

两个空都填 name。

运行过以后,再看一种答案
一种答案
%% 边界明确选择可见字段
public(User) ->
  #{name => User#user.name}.
想一想:为什么跨模块直接匹配 record tuple 位置很脆弱?

字段增删或顺序改变会影响所有知道布局的调用者;转换边界把变化收回模块内。

带走

记住三句

  1. 1

    record 适合局部固定结构。

  2. 2

    边界数据要让字段含义可见。

  3. 3

    转换函数隔离内部布局变化。

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