Erlang 速查指南:从 Shell 编译运行到进程并发、GenServer 与 EUnit 测试的完整实操手册
【免费下载链接】reference面向开发者的技术速查清单(Cheat Sheets)集合,整理常见技术、工具与开发流程,帮助快速查阅关键信息,提高开发效率。项目地址: https://gitcode.com/GitHub_Trending/referen/reference
本文基于 Quick Reference(referen/reference)技术速查仓库中的 Erlang 备忘清单 整理扩充,覆盖 Erlang 从 Shell 操作、模块编译运行,到并发进程模型、消息传递、分布式节点、OTP GenServer 行为与 EUnit 测试的完整实操路径。读完后,你可以独立完成 Erlang 程序的编译、运行、进程间通信、异常处理与测试编写,并将该清单作为日常开发中的速查依据。
一、Erlang 入门:Shell、编译与运行
Erlang 是一种用于构建并发、分布式和容错系统的编程语言,其程序运行在 BEAM 虚拟机之上,进程轻量、互不共享内存,天然适合高可用服务。在 Quick Reference 仓库中,这份清单通过 README.md 的导航入口与 Erlang 条目关联,随站点一同构建发布。
启动 Erlang Shell
在命令行直接输入erl即可进入交互式 Shell,进入后提示符形如1>,可直接输入表达式求值:
erl编译代码
Erlang 提供 Shell 内编译与命令行编译两种方式,编译产物为.beam字节码文件:
# 在 Erlang Shell 中编译 c(module). # 在命令行中编译 erlc module.erl运行代码
编译完成后,既可以在 Shell 中直接调用模块函数,也可以用erl启动一个不带 Shell 界面、自动执行指定函数并退出的一体化进程:
# 在 Erlang Shell 中运行 module:function(). # 从命令行运行 erl -noshell -s module function -s init stop其中-noshell表示不加载交互式 Shell,-s module function指定 VM 启动时自动调用的模块函数,-s init stop表示任务执行完毕后让init进程停止,从而干净地退出进程。这一命令行组合常用于把 Erlang 程序当作一次性脚本运行。
退出 Erlang Shell
q().二、代码结构:模块、导出与变量
模块定义
Erlang 的基本代码单元是模块(module),每个.erl文件对应一个模块,文件内声明模块名并导出需要对外暴露的函数(以函数名/参数个数的元度形式):
-module(module_name). -export([function_name/arity, ...]). function_name(Args) -> % Function body. Result.导出函数
-export/1指令决定哪些函数可以被外部模块调用,元度(arity)是函数唯一性的一部分:
-export([function1/0, function2/1]).注释
Erlang 使用%开头的单行注释:
% 单行注释 % 这是一个注释变量
变量名必须以大写字母开头,赋值时先绑定后使用,这与大多数动态语言的习惯一致,但 Erlang 中变量一旦绑定不可重新赋值(同一表达式内可用\做卫语句式的再绑定):
VarName = Value. % 变量名必须以大写字母开头 Age = 25. Name = "Alice".三、核心数据类型
Erlang 的常用数据类型涵盖了原子、数字、布尔值、字符串、元组、列表与字典(Map)。
原子
原子是带名称的常量,含空格等特殊字符时可用引号包裹:
atom. % 例子:atom, 'Atom with spaces'数字
整数与浮点数:
123. % 整数 3.14. % 浮点数布尔值
true. false.字符串
"Hello, World!".元组
元组适合承载结构化、定长的结果数据,Erlang 生态中{ok, Value}/{error, Reason}这类元组返回约定极为常见:
{ok, "Success"}.列表
列表是有序的、可分段的链表结构,[H|T]模式匹配可将列表拆分为表头与表尾:
[1, 2, 3]. [H|T] = [1, 2, 3]. % H = 1, T = [2, 3]字典 (Map)
Map 以键 => 值的形式组织,支持按键取值:
#{key1 => value1, key2 => value2}.四、控制结构与函数定义
条件语句
if...end根据布尔条件分支,最后一个true ->分支充当默认兜底,保证条件必须被穷尽:
if Condition1 -> Expression1; Condition2 -> Expression2; true -> DefaultExpression end.case 表达式
case...of对表达式结果做模式匹配分支,_通配分支同样保证穷尽性:
case Expression of Pattern1 -> Expression1; Pattern2 -> Expression2; _ -> DefaultExpression end.函数定义
函数由函数名(参数) -> 表达式体构成,同一函数可以按不同参数列表定义多个子句,靠模式匹配选择执行的子句:
% 无参函数 my_function() -> ok. % 有参函数 add(A, B) -> A + B.五、列表推导式(列表生成)
列表推导式是 Erlang 最惯用的集合操作语法,||左侧为映射表达式,右侧为数据源与过滤条件:
% 生成 1 到 10 的列表 [ X || X <- lists:seq(1, 10)]. % 生成 1 到 10 中的偶数 [ X || X <- lists:seq(1, 10), X rem 2 == 0].六、并发编程:进程、消息传递与进程间协作
并发是 Erlang 的核心竞争力。所有计算单元都是轻量的 Erlang 进程,进程之间不共享内存,唯一通信手段是消息传递,这使得竞态条件几乎不可能出现。
启动 / 创建进程
spawn启动一个新进程并返回其 PID(进程标识符):
spawn(Module, Function, Args). % 示例 Pid = spawn(fun() -> io:format("Hello from process~n") end).发送消息
用!向目标 PID 投递消息,发送方不会阻塞:
Pid ! Message. % 示例 Pid ! {hello, self()}.接收消息
receive表达式挂起当前进程,直到邮箱中收到能匹配某个子句的消息;after子句提供超时处理,避免进程无限等待:
receive Pattern1 -> Expression1; Pattern2 -> Expression2; after Timeout -> TimeoutExpression end.模式匹配
消息接收、赋值、函数子句头都依赖模式匹配,它是 Erlang 解构数据的基本方式:
{ok, Value} = {ok, 42}.链接进程
link建立的链接使两个进程命运绑定:一方异常退出会向另一方发送exit信号,这是构建监督树(supervision tree)的基础;unlink可解除绑定:
link(Pid). unlink(Pid).监控进程
与 link 不同,erlang:monitor建立的是单向观察关系:被监控进程退出时,监控方会收到一条{'DOWN', MonitorRef, process, Pid, Info}消息,而监控方本身不受影响。MonitorRef是监控引用,可用于取消监控:
MonitorRef = erlang:monitor(process, Pid). erlang:demonitor(MonitorRef).可以推断:link 适合"同生共死"的父子/监督关系,monitor 适合只关心"对方是否还活着"的旁路观察,两者在 OTP 程序中经常配合使用。
七、常用内置函数 (BIFs)
列表操作
lists:append(List1, List2). lists:map(Function, List). lists:filter(Function, List). lists:foldl(Function, Acc, List).配合匿名函数使用:
lists:map(fun(X) -> X * 2 end, [1, 2, 3]). lists:filter(fun(X) -> X rem 2 == 0 end, [1, 2, 3, 4]).元组操作
element(N, Tuple). setelement(N, Tuple, Value). tuple_size(Tuple).字符串操作
string:len(String). string:concat(String1, String2). string:tokens(String, Delimiters).string:len("Hello"). string:upper("hello").文件操作
file:read_file(Filename). file:write_file(Filename, Data). file:delete(Filename).带文件句柄的读写方式:
{ok, File} = file:open("test.txt", [write]). file:write(File, "Hello, file!"). file:close(File).八、综合示例:一个简单的回显服务器
以下模块把前面的进程、消息与receive知识串成一个最小可运行的服务器:start/0通过spawn启动服务进程,loop/0在循环中接收消息,{echo, Msg}触发回显,stop消息让服务器优雅退出,其余消息打印未知消息后继续循环:
-module(server). -export([start/0, loop/0]). start() -> spawn(fun loop/0). loop() -> receive {echo, Msg} -> io:format("Echo: ~p~n", [Msg]), loop(); stop -> io:format("Server stopping~n"), ok; _ -> io:format("Unknown message~n"), loop() end.使用方式:编译该模块后,在 Shell 中调用server:start()获得服务进程 PID,随后向其发送Pid ! {echo, "hi"}.即可看到回显输出,最后发送Pid ! stop.结束服务器。注意loop/0虽然已在-export中导出,但真正的入口语义是start/0,loop/0导出主要是便于调试时手动调用。
九、错误处理:try/catch 与异常类型
捕获异常
try...catch表达式把表达式求值与异常处理包裹在一起:
try Expression of Pattern -> Result catch Class:Reason -> Handler end.常见异常类型
Erlang 中有三类异常,catch子句需按类型分别处理:
throw:显式抛出的正常流程控制,如throw({error, code})error:运行时错误,如除零、函数未定义exit:进程退出信号,如链接进程崩溃引发的连锁退出
错误处理
按异常类型编写处理分支(与catch Class:Reason的写法对应):
try Expression of Pattern -> Result catch Type:Reason -> ErrorHandlingExpression end.在 Erlang 的容错哲学中,单个进程抛出异常只会终止它自己,而不会波及整个 VM;配合第六节的 link/monitor 机制与 OTP 监督树,"让进程崩溃、由监督者重启"(let it crash)成为构建高可用系统的主流手段。
十、分布式编程
Erlang 的分布式能力内建于语言层面:节点间通信在语法上与本地消息发送几乎一致。
启动分布式节点
-name指定节点名(主机名@域名形式),-setcookie设置节点间互信所必需的 Cookie,所有互通节点必须使用相同的 Cookie:
erl -name nodename@hostname -setcookie Cookie连接节点
net_adm:ping向远端节点发送探活请求,连通时返回pong:
net_adm:ping(Node).发送消息到远程节点
原文清单给出的示例形式为向远程节点发送消息:
{remote_process, 'remote_node@host'} ! Message.实际项目中更常见的写法是直接以节点名或远端 PID 作为消息接收方(语法上与本地发送完全一致):
Node ! Message. % Node 为 'remote_node@host' 形式的原子由于消息发送在本地与远程之间语法统一,把单节点程序扩展为分布式程序的成本非常低,这也是 Erlang 在电信、实时聊天等场景中流行的原因之一。
十一、OTP 框架:定义与使用 GenServer
GenServer 是 OTP(Open Telecom Platform)提供的基础行为(behaviour)之一,封装了"接收请求—处理—返回响应/更新状态"的服务循环,并自带进程名注册、链接与重启支持。
定义 GenServer
实现-behaviour(gen_server)后,需实现下列回调:init/1(初始化状态)、handle_call/3(同步请求)、handle_cast/2(异步消息)、handle_info/2(其他消息)、terminate/2(终止清理)、code_change/3(热升级):
-module(my_gen_server). -behaviour(gen_server). -export([start_link/0, init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). init([]) -> {ok, #state{}}. handle_call(Request, From, State) -> {reply, Reply, State}. handle_cast(Msg, State) -> {noreply, State}. handle_info(Info, State) -> {noreply, State}. terminate(Reason, State) -> ok. code_change(OldVsn, State, Extra) -> {ok, State}.使用 GenServer
客户端通过call(同步等待回复)与cast(异步投递)与服务器交互:
gen_server:start_link({local, Name}, Module, Args, Options). gen_server:call(ServerRef, Request). gen_server:cast(ServerRef, Msg).call适用于需要拿到结果的请求,cast适用于只通知不等待的场景;从源码结构看,start_link与调用者建立链接,服务器异常退出会通知启动方,这正是监督树能够感知并重启服务的前提。
十二、测试:编写与运行 EUnit
EUnit 是 Erlang 自带的单元测试框架。
编写 EUnit 测试
测试模块需要引入eunit.hrl头文件;普通函数是单个测试,以_结尾的函数(如complex_test_/0)返回子测试列表,实现参数化测试:
-module(module_name_tests). -include_lib("eunit/include/eunit.hrl"). simple_test() -> ?assertEqual(Expected, Actual). complex_test_() -> [ {"Test case 1", ?_assertEqual(Expected1, Actual1)}, {"Test case 2", ?_assertEqual(Expected2, Actual2)} ].其中?assertEqual断言两值相等,?_assertEqual是可在生成测试中使用的"惰性"版本。
运行 EUnit 测试
在命令行中以一次性进程方式运行指定模块的测试:
# 在命令行中运行 erl -eval "eunit:test(module_name)" -s init stop小结
这份清单的脉络——Shell 与编译运行、模块与数据类型、控制结构、并发消息传递、BIFs、错误处理、分布式、OTP GenServer、EUnit 测试——恰好覆盖了 Erlang 开发者的日常高频操作。在 Quick Reference 仓库中,它与 README.md 中登记的其他语言/工具速查页共同构成站点内容,通过 package.json 定义的refs-cli构建脚本与 netlify.toml 的构建配置完成静态站点发布,适合作为开发过程中随时打开对照的速查手册。
【免费下载链接】reference面向开发者的技术速查清单(Cheat Sheets)集合,整理常见技术、工具与开发流程,帮助快速查阅关键信息,提高开发效率。项目地址: https://gitcode.com/GitHub_Trending/referen/reference
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考