Erlang 从零 · 第 09 课(展开目录)
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 0940 分钟
把代码放进模块
把函数存进 `.erl` 文件,编译运行,并望见 BEAM 主线。
01 · 先看完整例子
保存为 village.erl
保存后,在同一目录打开 erl,运行 c(village).。
Erlang
%% 文件名与模块名保持一致
-module(village).
%% 允许模块外调用 greet/1
-export([greet/1]).
%% Name 是一个 binary
greet(Name) ->
<<"hello, ", Name/binary>>.先对照结果
c(village).编译成功时返回{ok,village}。village:greet(<<"A-Ming">>).返回<<"hello, A-Ming">>。
02 · 回头拆代码
从第一行往下读
-module(village).声明模块名;文件名应是village.erl。-export([greet/1]).中的/1表示greet接收一个参数。Name/binary把传入的 binary 接在固定文字后面。- 编译后用
模块:函数(参数)调用:village:greet(<<"A-Ming">>)。
03 · 认清新符号
符号不是暗号
-module(village).声明模块名是 village,文件应叫 village.erl。
-export([greet/1]).把接收 1 个参数的 greet 函数公开。
c(village).在 Erlang shell 中编译并加载 village.erl。
04 · 代码里的概念
把名字和意思对上
模块
一个 .erl 文件中的函数集合。模块名通常与文件名相同。
export
列出允许模块外部调用的函数,格式是 name/arity。
进程
BEAM 中独立运行、通过消息合作的小执行单元。
05 · 再说清楚
这些写法为什么有用
shell 像草稿纸。需要保存和复用的命名函数通常放进模块文件,文件名与模块名保持一致。
-export([greet/1]). 允许模块外调用 greet/1。这里必须写 /1,因为同名但参数个数不同的函数是不同函数。
模块能运行后,就可以进入 BEAM 的强项:小进程、消息、监督和故障恢复。这些属于下一条主线。
设计选择
为什么这样写
为什么先用模块组织函数,而不是先造 class?
模块给一组相关函数命名并形成公开边界。数据仍是显式参数,不藏在隐含的 this 里。
需要长期状态与生命周期时再引入进程;单纯把函数塞进 GenServer,只会多一层排队。
换一把尺子
从 Java、Python、JavaScript 看过来
Java
- 熟悉的起点
- class 同时装数据、方法和对象身份。
- BEAM 在意什么
- BEAM 模块不是对象;函数没有隐含接收者。
- 别带错直觉
- 模块也不会因为被调用就启动一个进程。
Python
- 熟悉的起点
- module 与 class 都能组织代码。
- BEAM 在意什么
- 这里先让 module/function/arity 成为稳定入口,状态另外安排。
- 别带错直觉
- OTP Application 也不是操作系统进程。
JavaScript
- 熟悉的起点
- ES module 导出函数或 class。
- BEAM 在意什么
- 编译后的模块由 BEAM 按模块名与 arity 调用。
- 别带错直觉
- Mix/rebar3 管项目,不等于 BEAM 或 OTP。
06 · 自己改一次
合上答案,动手
增加一个公开函数 farewell/1,返回 <<"bye, ", Name/binary>>。
练习起点
-module(village).
%% 在列表中再公开一个函数
-export([greet/1, ____]).
greet(Name) ->
<<"hello, ", Name/binary>>.
%% 补出新函数
____(Name) ->
<<"bye, ", Name/binary>>.目标结果: 重新编译后,village:farewell(<<"A-Ming">>). 返回 <<"bye, A-Ming">>。
卡住了,再看提示
导出列表的空填 farewell/1;函数定义的空只填 farewell。
运行过以后,再看一种答案
一种答案
-module(village).
%% 两个函数都接收一个参数
-export([greet/1, farewell/1]).
greet(Name) ->
<<"hello, ", Name/binary>>.
%% 最后一条函数定义用句点结束
farewell(Name) ->
<<"bye, ", Name/binary>>.想一想:为什么导出列表写 `greet/1`,而不是只写 `greet`?
Erlang 用函数名和参数个数共同确认一个函数。greet/1 与 greet/2 是两个不同函数。
带走
记住三句
- 1
模块名与
.erl文件名保持一致。 - 2
导出列表用
name/arity准确写出函数。 - 3
学完语法后,再进入 BEAM 的进程与消息。
本课结束代码也跑过一次,就把这一课收好。