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

把约定留在源码旁

补 EDoc、typespec、EUnit 与跨模块成功测试。

01 · 先看完整例子

为公开统计入口补约定

完整模块让 EDoc、Dialyzer 与调用者看到同一份约定。

Erlang
-module(trail_stats).
-export([count/1]).

-type stats() :: #{
  lines := non_neg_integer(),
  words := non_neg_integer(),
  chars := non_neg_integer()
}.
-export_type([stats/0]).

%% @doc 统计已经清洗的 UTF-8 binary 行。
%% 换行不计入 chars。
-spec count([binary()]) -> stats().
count(Lines) ->
  %% 返回值与 stats() 的三个必需键一致
  #{
    lines => length(Lines),
    words => lists:sum([
      length(string:lexemes(Line, " 	")) || Line <- Lines
    ]),
    chars => length(unicode:characters_to_list(iolist_to_binary(Lines)))
  }.
先对照结果
  • rebar3 edoc 可生成公开 count/1 与 stats/0 文档。
02 · 回头拆代码

从第一行往下读

  1. map type 用 := 表示三项必须存在。
  2. 导出类型让其他模块的 spec 可以引用 trail_stats:stats()。
  3. 为不存在文件补 error 测试,为正常文件补端到端测试。
03 · 认清新符号

符号不是暗号

%% @doc

为后面的公开函数写 EDoc 说明。

-type stats() :: ...

为统计 map 起类型名。

rebar3 edoc

生成项目 API 文档。

04 · 代码里的概念

把名字和意思对上

01

EDoc

从 Erlang 源码注释与 specs 生成 API 文档的工具。

02

集成测试

让多个模块一起运行,检查边界连接。

05 · 再说清楚

这些写法为什么有用

代码能运行,还需要把约定留给以后。EDoc 注释说明责任,typespec 写 term 形状,测试保存例子。

文档不要逐行抄实现。说清输入、输出、编码和错误边界就够。

单元测试守一个模块;集成测试从临时文件走到统计结果,检查两段接口是否接得上。

06 · 自己改一次

合上答案,动手

补全 Parser 公开 spec 的成功类型。

练习起点
-spec read(file:filename_all()) ->
  {ok, ____} | {error, term()}.

目标结果: 成功值是 binary list,即 [binary()]。

卡住了,再看提示

方括号表示元素类型组成的 list。

运行过以后,再看一种答案
一种答案
%% 成功时返回清洗后的行 list
-spec read(file:filename_all()) ->
  {ok, [binary()]} | {error, term()}.
想一想:文档是否应该写出 `word_count/1` 每一步怎样实现?

不必。它是私有细节;公开文档重点是 count/1 的输入输出和计数规则。

带走

记住三句

  1. 1

    文档讲边界和约定。

  2. 2

    typespec 覆盖真实返回分支。

  3. 3

    集成测试检查模块之间的连接。

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