Erlang
Built for systems that never stop.
Erlang was created at Ericsson to run telephone switches that must never go down. Its virtual machine (the BEAM) runs millions of lightweight processes, isolates failures and lets you update code while the system runs. WhatsApp, RabbitMQ and many telecom and messaging systems are built on it.
Why learn Erlang?
Erlang's ideas of isolated processes and "let it crash" supervision are exactly what large AI serving systems need for reliability.
- Massive concurrency: millions of processes on one machine.
- Fault tolerance: supervisors restart failed processes automatically.
- Proven: decades of use in systems that need very high uptime.
Set up and run your first program
Install Erlang/OTP from your package manager. Use erl for the shell and rebar3 for projects.
sudo apt install erlang
erlc hello.erl
erl -noshell -s hello start -s init stop-module(hello).
-export([start/0]).
start() ->
Name = "Ada",
io:format("Hello, AI! Welcome to Erlang, ~s.~n", [Name]).Values, atoms and pattern matching
Variables are bound once and never change. Atoms like ok and error are named constants. = is a pattern match.
-module(basics).
-export([run/0]).
run() ->
Layers = 32,
Model = "llama3.1",
{ok, Vram} = {ok, 80},
[First | Rest] = [h100, h200, b200],
io:format("~s: ~p layers, ~p GB~n", [Model, Layers, Vram]),
io:format("~p then ~p~n", [First, Rest]),
io:format("~.1f GB~n", [8.03e9 * 2 / 1.0e9]).Functions and guards
Functions can have several clauses chosen by pattern matching and guards. Loops are written with recursion.
-module(funcs).
-export([precision/1, sum/1, params/2]).
precision(32) -> "FP32";
precision(16) -> "FP16";
precision(Bits) when Bits < 16 -> "low precision".
sum([]) -> 0;
sum([H | T]) -> H + sum(T).
params(Layers, Width) -> 12 * Layers * Width * Width.Lists, maps and higher-order functions
The lists module offers map, filter and fold. List comprehensions and maps (#{}) make data handling concise.
-module(colls).
-export([run/0]).
run() ->
Vram = #{h100 => 80, h200 => 141, rtx5090 => 32},
Big = [Name || {Name, Gb} <- maps:to_list(Vram), Gb >= 80],
Total = lists:sum(maps:values(Vram)),
Squares = lists:map(fun(X) -> X * X end, lists:seq(1, 5)),
io:format("~p ~p ~p~n", [Big, Total, Squares]).Processes and messages
spawn starts a process; ! sends a message; receive waits for one. Processes share no memory, so there are no locks.
-module(procs).
-export([run/0, worker/0]).
worker() ->
receive
{From, {square, N}} -> From ! {self(), N * N}, worker();
stop -> ok
end.
run() ->
Pid = spawn(?MODULE, worker, []),
Pid ! {self(), {square, 12}},
receive {Pid, Result} -> io:format("result: ~p~n", [Result])
after 1000 -> io:format("timeout~n")
end,
Pid ! stop.OTP: gen_server and supervisors
OTP is Erlang's framework for reliable systems. A gen_server holds state; a supervisor restarts it if it crashes.
-module(counter).
-behaviour(gen_server).
-export([start_link/0, add/1, value/0]).
-export([init/1, handle_call/3, handle_cast/2]).
start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, 0, []).
add(N) -> gen_server:cast(?MODULE, {add, N}).
value() -> gen_server:call(?MODULE, value).
init(N) -> {ok, N}.
handle_cast({add, X}, N) -> {noreply, N + X}.
handle_call(value, _From, N) -> {reply, N, N}.AI with Erlang
Erlang is not a model-training language, but its strengths fit AI infrastructure: handling huge numbers of concurrent requests, routing jobs to GPU workers and keeping chat services alive. Many teams use Elixir's Nx and Bumblebee on the same BEAM for model serving, and call LLM APIs from Erlang with built-in HTTP support.
The essential libraries
Parallel work with processes
Spawn one process per job and gather results, the pattern behind fan-out inference requests.
-module(fanout).
-export([run/0]).
score(Prompt) -> timer:sleep(100), length(Prompt). % stand-in for a model call
run() ->
Parent = self(),
Prompts = ["hello", "tensor cores", "agents"],
Pids = [spawn(fun() -> Parent ! {self(), score(P)} end) || P <- Prompts],
Results = [receive {Pid, R} -> R end || Pid <- Pids],
io:format("~p~n", [Results]). % all done in about 100 msBuild an AI agent in Erlang
OTP 27 added a built-in json module, so with httpc you can write a tool-calling agent using only the standard library.
-module(agent).
-export([run/0]).
-define(URL, "http://localhost:11434/v1/chat/completions").
tools() ->
[#{type => <<"function">>, function => #{name => <<"get_weather">>, description => <<"Weather for a city">>,
parameters => #{type => <<"object">>, properties => #{city => #{type => <<"string">>}}, required => [<<"city">>]}}}].
chat(Messages) ->
Body = iolist_to_binary(json:encode(#{model => <<"llama3.1">>, messages => Messages, tools => tools()})),
{ok, {_, _, Resp}} = httpc:request(post, {?URL, [], "application/json", Body}, [], [{body_format, binary}]),
#{<<"choices">> := [#{<<"message">> := Msg} | _]} = json:decode(Resp),
Msg.
loop(_, 0) -> io:format("gave up~n");
loop(Messages, N) ->
Msg = chat(Messages),
case maps:get(<<"tool_calls">>, Msg, null) of
Calls when is_list(Calls), Calls =/= [] ->
Replies = [begin
#{<<"city">> := City} = json:decode(maps:get(<<"arguments">>, maps:get(<<"function">>, C))),
#{role => <<"tool">>, tool_call_id => maps:get(<<"id">>, C),
content => <<"It is 24 C and sunny in ", City/binary, ".">>}
end || C <- Calls],
loop(Messages ++ [Msg | Replies], N - 1);
_ -> io:format("~ts~n", [maps:get(<<"content">>, Msg)])
end.
run() ->
inets:start(),
loop([#{role => <<"user">>, content => <<"Should I take an umbrella in Lahore today?">>}], 5).Run each conversation in its own process so one slow model call never blocks others.
ollama pull llama3.1, and keep it running. The code talks to its OpenAI-compatible API at http://localhost:11434/v1, so the same program also works with vLLM, llama.cpp server or a cloud API by changing the URL, model name and adding an API key.Erlang learning roadmap
Tick items off as you go. Your progress is saved in this browser.
Beginner
Intermediate
Advanced
Projects to build
Erlang cheat sheet
| Task | Syntax |
|---|---|
| Bind | X = 5, |
| Atom | ok |
| Tuple | {ok, Value} |
| List comprehension | [X*X || X <- L] |
| Spawn | Pid = spawn(fun() -> ... end) |
| Send / receive | Pid ! Msg, receive M -> M end |
Common mistakes
- Variables start with uppercase; lowercase words are atoms.
- Punctuation matters: commas between expressions, semicolons between clauses, a period at the end.
- Strings in double quotes are lists of characters; use binaries
<<"text">>for efficiency.
Where to go next
- Learn You Some Erlang — free book
- Erlang docs — official
- Adopting Erlang — production guide
On TensorCore.si: Elixir volume, Go volume.
Keyboard: [ previous language · ] next language · / search the library