Erlang Cheat Sheet
Core Erlang syntax covering modules, pattern matching, recursion, and the actor-model process/message-passing primitives.
Modules & Functions
A minimal Erlang module.
%% hello.erl-module(hello).-export([world/0]).world() -> io:format("Hello, World!~n").%% In the shell:%% 1> c(hello).%% 2> hello:world().
Pattern Matching
Single-assignment variables and function clauses.
%% Variables bind once (single assignment)X = 5,{A, B} = {1, 2}, %% tuple pattern matchfactorial(0) -> 1;factorial(N) when N > 0 -> N * factorial(N - 1).describe({ok, Value}) -> Value;describe({error, Reason}) -> Reason.
Processes & Messages
Spawning lightweight processes and sending messages.
%% Spawn a process and send it messagesPid = spawn(fun loop/0),Pid ! {self(), hello},loop() -> receive {From, hello} -> From ! {self(), world}, loop(); stop -> ok end.
Modules & Attributes
Common module-level declarations.
- -module(name).- Declares the module; must match the filename
- -export([f/1]).- Exposes function f with arity 1 publicly
- -record(point, {x, y}).- Defines a record type
- io:format("~p~n", [Term])- Print a formatted term followed by a newline
- spawn/1- Creates a new lightweight process
- receive ... end- Blocks waiting for a matching message
Data Types
Erlang's built-in term types.
- Atom- Constant literal, e.g. ok, error, undefined
- Tuple- Fixed-size grouping, e.g. {ok, Value}
- List- e.g. [1, 2, 3], built from recursive cons cells
- Binary- e.g. <<1,2,3>>, a raw byte sequence
- PID- Process identifier returned by spawn
- Map- e.g. #{key => value}, a key/value store
Records
Named tuples with field access via record syntax.
-record(user, {name, age = 0, email}).%% createU = #user{name = "Ada", age = 36},%% access a fieldName = U#user.name,%% update (returns a new record)U2 = U#user{age = 37},%% pattern match in a function headis_adult(#user{age = A}) when A >= 18 -> true;is_adult(_) -> false.
gen_server Skeleton
OTP generic server behaviour with callbacks.
-module(counter).-behaviour(gen_server).-export([start_link/0, inc/0]).-export([init/1, handle_call/3, handle_cast/2]).start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, 0, []).inc() -> gen_server:cast(?MODULE, inc).init(N) -> {ok, N}.handle_call(get, _From, N) -> {reply, N, N}.handle_cast(inc, N) -> {noreply, N + 1}.
Supervisor
Define a supervision tree with restart strategy.
-module(my_sup).-behaviour(supervisor).-export([start_link/0, init/1]).start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).init([]) -> SupFlags = #{strategy => one_for_one, intensity => 5, period => 10}, Child = #{id => counter, start => {counter, start_link, []}, restart => permanent, type => worker}, {ok, {SupFlags, [Child]}}.
Errors & try/catch
Handle exceptions and use links for fault propagation.
try risky(X) of Result -> {ok, Result}catch throw:Reason -> {thrown, Reason}; error:badarg -> {error, bad_argument}; exit:Why -> {exit, Why}after cleanup()end.%% crash on purpose - let the supervisor restartprocess(0) -> error(division_by_zero);process(N) -> 100 div N.
Common BIFs & Guards
Built-in functions and guard tests used everywhere.
- is_atom/1, is_list/1- type-test guards usable in function heads
- length/1- number of elements in a list
- element(N, Tuple)- 1-indexed tuple element access
- lists:map/2, lists:foldl/3- higher-order list processing
- maps:get/2, maps:put/3- read/update immutable maps
- spawn/3, spawn_link/3- start a new process, optionally linked
- self()- pid of the current process
- erlang:now/0, os:timestamp/0- timestamps for timing and ids
Lean on 'let it crash' — wrap risky work in a supervised process instead of defensive try/catch everywhere; OTP supervisors restart failed processes automatically.