Bases
Hello World et Modules
Chaque fichier Erlang est un module déclaré avec -module(name). Les fonctions exportées sont listées avec -export([name/arity]). L'arité fait partie de l'identité de la fonction — greet/0 et greet/1 sont distinctes. Compilez avec c(Module).
% hello.erl
-module(hello).
-export([greet/1, greet/0]).
greet() ->
greet("World").
greet(Name) ->
io:format("Hello, ~s!~n", [Name]).
% Compile & run in the shell:
% c(hello).
% hello:greet("Alice"). %=> Hello, Alice!Variables et Atomes
Les variables commencent par une majuscule (ou _) et sont à assignation unique. Re-matcher une variable liée est une assertion : X = X+1 lève une erreur badmatch. Les atomes sont des constantes nommées commençant par une minuscule, comparées par identité.
Name = "Alice". % variables start with uppercase
Age = 30. % bound once — single assignment!
% Age = 31. % ** exception error: no match of RHS
% Atoms start with lowercase (or are quoted)
ok. % the ok atom (common return value)
error. % another atom
'This is an atom'. % quoted atom with spaces
true, false, undefined % built-in atoms
% Pattern matching binds and asserts
{ok, Result} = {ok, 42}. % Result = 42
{error, _} = {error, badarg}. % _ matches and discardsTuples et Records
Les tuples sont des conteneurs de taille fixe écrits {a,b,c}. Les records sont des tuples avec champs nommés, déclarés avec -record. Mettre à jour un champ crée un nouveau record (données immuables). Le premier élément est conventionnellement un atome étiquette.
% Tuples (fixed-size, positional)
Point = {point, 3, 4}.
{point, X, Y} = Point. % X=3, Y=4
element(2, Point). %=> point
% Records (syntactic sugar over tuples)
-record(person, {name, age, city = "Unknown"}).
P = #person{name = "Alice", age = 30}.
P#person.name. %=> "Alice"
P2 = P#person{age = 31}. % update field
{person, Name, Age, _} = P. % underlying tupleListes et Compréhensions
Les listes sont des listes chaînées construites avec [Head|Tail]. ++ concatène (O(n) à gauche), -- retire des éléments. Les compréhensions [Expr || Qualificateur1, ...] supportent générateurs (X <- List) et filtres (expressions booléennes).
[1,2,3] ++ [4,5]. %=> [1,2,3,4,5] (append)
[1,2,3] -- [2]. %=> [1,3] (subtract)
hd([1,2,3]). %=> 1 (head)
tl([1,2,3]). %=> [2,3] (tail)
% Pattern matching on lists
[Head | Tail] = [1,2,3]. % Head=1, Tail=[2,3]
[A,B|Rest] = [1,2,3,4]. % A=1, B=2, Rest=[3,4]
% List comprehensions
[ X*2 || X <- [1,2,3,4] ]. %=> [2,4,6,8]
[ X || X <- [1..10], X rem 2 =:= 0 ]. % evens: [2,4,6,8,10]
[ {A,B} || A <- [1,2], B <- [a,b] ]. % cartesian productFonctions et Pattern Matching
Les fonctions sont définies en multiples clauses séparées par ;. Les clauses sont essayées dans l'ordre ; la première dont le motif ET la garde réussissent est utilisée. Les tuples étiquetés {ok, Val} / {error, Reason} signalent succès/échec sans exceptions.
-module(math_tools).
-export([factorial/1, fib/1, classify/1]).
% Multiple clauses selected by pattern matching
factorial(0) -> 1;
factorial(N) when N > 0 -> N * factorial(N - 1).
% Fibonacci with guards
fib(0) -> 0;
fib(1) -> 1;
fib(N) when N > 1 -> fib(N-1) + fib(N-2).
% Tagged returns (common Erlang idiom)
classify(X) when X < 0 -> {negative, X};
classify(0) -> zero;
classify(X) when X > 0 -> {positive, X}.Maps
Les maps (depuis R17) sont des dictionnaires clé-valeur. Utilisez => pour définir/insérer et := pour mettre à jour (la clé doit exister). En pattern matching seul := est autorisé. maps:get/2 lève badarg sur clé absente ; utilisez maps:get/3 pour une valeur par défaut.
% Create
M = #{ name => "Alice", age => 30, role => admin }.
% Lookup
maps:get(name, M). %=> "Alice"
maps:get(city, M, "Unknown"). % default if missing
{ok, V} = maps:find(age, M). %=> {ok, 30}
% Update / add (immutable — returns new map)
M2 = M#{ age => 31, city => "Paris" }.
% Pattern match on maps
#{ name := N } = M. % N = "Alice" (:= for matching, => for setting)
#{ age := A, role := R } = M.Pattern Matching et Gardes
Pattern Matching dans les Têtes de Fonction
Erlang sélectionne une clause en matchant les arguments de haut en bas. Les motifs peuvent lier des variables, affirmer l'égalité avec des littéraux et ignorer des champs avec _. Le même mécanisme s'applique à receive, case et =.
% Select clause by structure of argument
handle({ping, From}) -> From ! pong;
handle({echo, Msg}) -> Msg;
handle({add, A, B}) -> A + B;
handle(stop) -> bye.
% Tuple destructuring
-record(rect, {w, h}).
area(#rect{w=W, h=H}) -> W * H.
% List patterns with literal values
greet_first([H|_]) -> io:format("Hi ~p~n", [H]);
greet_first([]) -> ok.Expressions case
case évalue une expression et la compare à une séquence de motifs. Chaque clause peut avoir une garde. C'est l'outil de contrôle standard quand la ramification dépend d'une valeur calculée. Incluez toujours une clause _ sauf si vous voulez un crash.
classify_file(Size) ->
case Size of
0 -> empty;
N when N < 1024 -> small;
N when N < 1048576 -> medium;
_ -> large
end.
% Matching on tagged tuples
Result = case file:read_file("a.txt") of
{ok, Bin} -> {ok, binary_to_list(Bin)};
{error, enoent} -> {error, missing};
{error, Reason} -> {error, Reason}
end.Gardes
Les gardes restreignent quand une clause correspond. Elles se limitent à un ensemble de fonctions intégrées pour toujours terminer sans effets de bord. Virgule = ET, point-virgule = OU. Préférez le pattern matching aux gardes quand possible.
% Guards go after 'when' and may use built-in guard tests (BIFs)
max(A, B) when A >= B -> A;
max(_, B) -> B.
% Multiple guard tests: ',' = AND, ';' = OR
classify(N) when N > 0, N < 100 -> in_range;
classify(N) when N =< 0; N >= 100 -> out_of_range.
% Allowed guard BIFs include comparison, arithmetic, type tests:
% is_integer/1, is_atom/1, is_list/1, is_map/1,
% length/1, map_size/1, abs/1, element/2, hd/1, tl/1
% (No user functions — guards must be side-effect free and terminate!)Syntaxe de Bits et Binaires
La syntaxe de bits << Seg1, Seg2, ... >> empaquète/dépaquette des binaires par champs de bits. Chaque segment est Value:Size/TypeSpecifier. Cela rend Erlang exceptionnellement bon pour analyser protocoles réseau et formats de fichier.
% Pack a 16-bit length + 8-bit type + payload
Bin = << Length:16, Type:8, Payload/binary >>.
% Unpack with the same pattern
<< L:16, T:8, Rest/binary >> = Bin.
% Bit fields & endianness
<< N:32/big-unsigned-integer >> = <<0,0,1,1>>. % N = 261
<< N:32/little-signed-integer >> = <<1,0,0,0>>. % N = 1
% IPv4 header parsing example
<< Version:4, IHL:4, _TOS:8, TotalLen:16,
_/binary >> = Packet.Programmation Concurrente
spawn — Démarrer un Processus
spawn(M,F,A) crée un nouveau processus BEAM — un thread vert léger avec son propre heap et sa mailbox. Les processus ne partagent pas de mémoire et communiquent uniquement par passage de messages. BEAM peut gérer des dizaines de millions de processus.
% spawn/3 starts a new lightweight process
Pid = spawn(Module, Function, ArgsList).
Pid = spawn(fun() -> timer:sleep(1000), io:format("done~n") end).
% Every process has a PID; processes share NO memory
% (BEAM processes are NOT OS threads — millions are possible)
% self() returns the current process's PID
io:format("I am ~p~n", [self()]).
% is_process_alive/1 checks liveness
case is_process_alive(Pid) of
true -> ok;
false -> {error, dead}
end.send (!) — Passage de Messages
Pid ! Msg envoie asynchronement un message à la mailbox du destinataire. La livraison est garantie et l'ordre préservé entre la même paire de processus. ! retourne le message, permettant les broadcasts. Les processus peuvent être enregistrés sous un nom atome.
% Send a message: Pid ! Message
Pid ! {hello, self()}.
Pid ! ping.
Pid ! {compute, 1, 2, 3}.
% ! always returns the message itself, so you can broadcast:
[ P ! broadcast || P <- AllPids ].
% Send to a registered process (by atom name)
registered_name ! {request, Ref, self()}.
% Register / unregister a name for a PID
register(server, Pid).
unregister(server).
whereis(server). %=> Pid | undefinedreceive — Messages par Pattern Matching
receive scanne la mailbox dans l'ordre pour le premier message correspondant ; sinon se suspend. La clause after fixe un timeout (0 = non bloquant, infinity = éternel). Un pattern serveur courant est un loop() tail-récursif qui s'appelle lui-même.
% receive blocks until a matching message arrives
loop() ->
receive
{ping, From} ->
From ! pong,
loop();
{echo, Msg} ->
io:format("echo: ~p~n", [Msg]),
loop();
stop ->
ok
end.
% receive with a timeout
receive
{data, X} -> process(X)
after 5000 ->
{error, timeout}
end.
% Flush mailbox (drain all messages)
flush() ->
receive _ -> flush() after 0 -> ok end.Receive Sélectif et Non Sélectif
receive est sélectif : il cherche le premier message correspondant même si des messages plus anciens existent. Idéal pour matcher une réponse spécifique (avec un Ref unique), mais les messages non correspondants s'accumulent. gen_server évite cela.
% Selective receive: skips non-matching messages
get_reply(Ref) ->
receive
{Ref, Reply} -> Reply % only matches the reply with our Ref
after 5000 ->
{error, timeout}
end.
% Problem: other messages stay in the mailbox, slowing future receives.
% For high-throughput servers use a selective receive library or
% gen_server (which keeps its mailbox clean).
% Non-selective (drain in order):
drain_all() ->
receive
Msg -> [Msg | drain_all()]
after 0 ->
[]
end.Requête / Réponse avec Références
Le pattern call associe chaque requête à une référence unique (make_ref/0). Le client envoie {call, Ref, self(), Request} et attend une réponse étiquetée avec le même Ref. C'est la base de gen_server:call.
% Client: send a tagged request, await the matching reply
call(Server, Request) ->
Ref = make_ref(),
Server ! {call, Ref, self(), Request},
receive
{Ref, Reply} -> Reply
after 5000 ->
exit(timeout)
end.
% Server side:
handle({call, Ref, From, Request}, State) ->
Reply = do_work(Request),
From ! {Ref, Reply},
State.Récursivité Terminale et Boucle Serveur
Les processus BEAM maintiennent l'état par récursivité terminale : la boucle s'appelle avec le nouvel état comme dernière expression, sans croître la pile. Chaque clause receive se termine par un appel terminal à loop/1 avec l'état mis à jour.
% A counter server — each message mutates state via recursion
start_counter(Init) -> spawn(fun() -> counter_loop(Init) end).
counter_loop(N) ->
receive
inc -> counter_loop(N + 1);
dec -> counter_loop(N - 1);
{get, From} -> From ! {value, N}, counter_loop(N);
reset -> counter_loop(0)
end.
% Caller:
Pid = start_counter(0).
Pid ! inc.
Pid ! {get, self()}.
receive {value, V} -> io:format("count = ~p~n", [V]) end.Liens, Moniteurs et Erreurs
link / unlink — Lien Bidirectionnel
link/1 crée un lien bidirectionnel : si un processus se termine, l'autre reçoit un signal de sortie. Par défaut ce signal crash aussi le récepteur. process_flag(trap_exit, true) convertit les signaux en messages {'EXIT', From, Reason}. spawn_link/1 fait les deux atomiquement.
% link/1 links current process to another
Pid = spawn(fun() -> ... end),
link(Pid).
% If either dies, the other gets an exit signal.
% Default behavior: the linked partner also exits.
% spawn_link does both atomically:
Pid = spawn_link(fun() -> ... end).
% Trap exits to convert death signals into messages:
process_flag(trap_exit, true).
receive
{'EXIT', From, Reason} -> io:format("~p died: ~p~n", [From, Reason])
end.
unlink(Pid). % remove the link (best-effort)monitor / demonitor — Moniteur Unidirectionnel
monitor(process, Pid) est unidirectionnel : seul l'appelant reçoit un message 'DOWN'. Préférable à link quand on veut seulement observer. Chaque moniteur retourne un Ref unique.
% monitor/1: one-way — we observe Pid, but it doesn't observe us
Ref = monitor(process, Pid).
% If Pid dies, we receive:
% {'DOWN', Ref, process, Pid, Reason}
receive
{'DOWN', Ref, process, Pid, normal} -> ok;
{'DOWN', Ref, process, Pid, Reason} -> {error, Reason}
end.
% demonitor/1 removes the monitor
demonitor(Ref).
% demonitor/2 with [flush] also drains any pending 'DOWN' message
demonitor(Ref, [flush]).Signaux exit et Raisons
exit(Reason) termine le processus courant ; exit(Pid, Reason) envoie un signal à Pid. La raison compte : normal et shutdown ne se propagent pas, les autres se propagent et tuent les liés (sauf s'ils trappent). kill est intraptable — dernier recours.
% Send an exit signal explicitly:
exit(Pid, Reason). % tell Pid to exit with Reason
% Terminate the current process:
exit(Reason). % Reason != normal kills linked partners
% exit/2 with the atom 'kill' is untrappable:
exit(Pid, kill). % Pid dies even if it traps exits.
% (Only the kernel-supervised 'kill' reason cannot be trapped.)
% Reasons:
% normal -> linked partners do NOT die
% shutdown -> linked partners do NOT die (used in graceful stop)
% {shutdown, Term} -> graceful with reason, also non-propagating
% anything else -> linked partners also die (unless trapping)try / catch et Exceptions
try ... catch Class:Reason gère trois classes : throw (throw/1 explicite), error (erreurs runtime), exit (exit/1). La clause after s'exécute toujours pour le nettoyage. erlang:get_stacktrace() capture la pile d'appels.
try
{ok, Bin} = file:read_file(Path),
binary_to_list(Bin)
catch
throw:Term -> {thrown, Term};
error:Reason -> {error, Reason, erlang:get_stacktrace()};
exit:Reason -> {exit, Reason}
after
% always runs (cleanup), regardless of success/failure
file:close(Fd)
end.
% Raise exceptions:
throw({bad, Value}). % catchable as throw
erlang:error(badarg). % catchable as error
exit(timeout). % catchable as exitPattern Superviseur (Manuel)
Un superviseur trappe les exits, démarre son enfant avec spawn_link et le redémarre en recevant {'EXIT', Pid, _}. La philosophie « let it crash » — superviseur simple, workers qui échouent vite — est le cœur de la tolérance aux pannes d'Erlang.
% A minimal supervisor: start a child, restart on death
start_sup(Mod) ->
process_flag(trap_exit, true),
Pid = spawn_link(fun() -> Mod:run() end),
sup_loop(Mod, Pid).
sup_loop(Mod, Pid) ->
receive
{'EXIT', Pid, Reason} ->
io:format("child died ~p, restarting~n", [Reason]),
NewPid = spawn_link(fun() -> Mod:run() end),
sup_loop(Mod, NewPid);
{stop, From} ->
exit(Pid, shutdown),
From ! ok
end.OTP gen_server (Brève)
Squelette du Behaviour gen_server
Implémenter -behaviour(gen_server) fait vérifier par le compilateur les fonctions callback requises. L'état passe par init/1 -> handle_call/3 (synchrone) / handle_cast/2 (asynchrone) -> terminate/2. {local, Name} permet d'appeler par atome.
-module(counter).
-behaviour(gen_server).
-export([start_link/0, inc/0, get/0]).
-export([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, 0, []).
inc() -> gen_server:cast(?MODULE, inc).
get() -> gen_server:call(?MODULE, get).
init(N) -> {ok, N}.
handle_call(get, _F, N) -> {reply, N, N}.
handle_cast(inc, N) -> {noreply, N + 1}.
handle_info(_, N) -> {noreply, N}.
terminate(_, _) -> ok.
code_change(_, N, _) -> {ok, N}.call vs cast
call est synchrone et bloque l'appelant jusqu'à la réponse (ou timeout 5s). cast est asynchrone et retourne toujours ok immédiatement. call repose sur le pattern requête/réponse avec référence.
% Synchronous call — waits for reply, crashes on server death
{ok, Value} = gen_server:call(Server, {get, Key}).
% Asynchronous cast — fire and forget
ok = gen_server:cast(Server, {put, Key, Value}).
% Call supports timeout and monitor under the hood:
gen_server:call(Server, Req, 5000).
% Server replies in handle_call:
handle_call({get, Key}, From, State) ->
{reply, maps:get(Key, State), State}.
% handle_cast never replies:
handle_cast({put, K, V}, State) ->
{noreply, maps:put(K, V, State)}.handle_info et Autres Callbacks
handle_info/2 capture les messages non call/cast — tout Pid ! Msg, 'DOWN', timeouts et données socket. terminate/2 libère les ressources (non garanti sur kill brutal). code_change/3 permet le célèbre hot code upgrade sans interruption.
% handle_info/2 handles messages NOT from call/cast:
% - plain messages from other processes (Pid ! Msg)
% - 'DOWN' messages from monitors
% - timeouts sent by gen_server:start(..., Timeout)
handle_info({'DOWN', Ref, process, Pid, Reason}, State) ->
io:format("monitored ~p died: ~p~n", [Pid, Reason]),
{noreply, State};
handle_info({tcp, Socket, Data}, State) ->
%% handle incoming socket data
{noreply, State};
handle_info(Info, State) ->
{noreply, State}.
% terminate/2 runs on shutdown — close fds, flush, etc.
terminate(Reason, State) ->
io:format("shutting down: ~p~n", [Reason]),
ok.
% code_change/3 enables hot code upgrade.
code_change(_OldVsn, State, _Extra) -> {ok, State}.Structure d'Application (OTP)
Une application OTP est un composant autonome avec un .app décrivant modules, dépendances et module d'entrée (mod). start/2 retourne le PID du superviseur racine. Les releases empaquettent les applications en un système exécutable.
% myapp.app — application resource file
{application, myapp,
[{description, "Demo app"},
{vsn, "1.0.0"},
{modules, [myapp_app, myapp_sup, counter]},
{registered, [myapp_sup]},
{applications, [kernel, stdlib]},
{mod, {myapp_app, []}},
{env, [{port, 4000}]}
]}.
% myapp_app.erl — application behaviour
-module(myapp_app).
-behaviour(application).
-export([start/2, stop/1]).
start(_StartType, _StartArgs) -> myapp_sup:start_link().
stop(_State) -> ok.
% Start everything:
% application:start(myapp).
% Or with release: erl -eval 'application:ensure_all_started(myapp)'Snippets Erlang associés
Copy-paste ready code for common tasks.
Processus Ping-Pong
Deux processus se passent des messages avec spawn et receive.
Compteur avec gen_server
Construit un serveur avec état avec le behaviour gen_server.
Arbre de superviseurs
Définit un superviseur qui démarre des workers et les redémarre en cas de crash.
Receive sélectif avec références
Extrait une réponse spécifique parmi de nombreux messages avec une référence unique.
Liens, capture d'exit et « let it crash »
Utilise link et trap_exit pour détecter la mort d'un processus et se rétablir.
Serveur avec état par récursion terminale
Maintient un état mutable dans un processus en le filant à travers des appels récursifs.
Map parallèle avec rpc:pmap
Applique une fonction à chaque élément de liste en parallèle entre processus.
Mise à jour de code à chaud
Recharge le code d'un module sans arrêter le système en cours.
Was this helpful?