Erlang — это не просто язык программирования. Это целая экосистема, созданная компанией Ericsson для разработки отказоустойчивых, распределенных, параллельных систем с высокой доступностью, где время простоя измеряется минутами в год. Если ваша задача — создание высоконагруженных backend-систем, мессенджеров, игровых серверов или систем телекома, понимание Erlang открывает путь к архитектурам, устойчивым к сбоям по замыслу.
Фундамент Erlang — это функциональный язык с динамической типизацией и строгой оценкой (eager evaluation). Начнем с базовых строительных блоков. Все данные в Erlang — это термы (terms). Простые типы включают числа (целые и числа с плавающей точкой, причем целые имеют неограниченную точность), атомы (atom) — константы, чье имя является их значением (например, ok, error, my_atom), и бинарные данные (binaries) для эффективной работы с битовыми последовательностями.
Списки (lists) и кортежи (tuples) — ключевые структуры данных. Список — это последовательность элементов, заключенная в квадратные скобки. Он часто строится с использованием головы (head) и хвоста (tail).
Пример:
List = [1, 2, 3, 4].
[Head | Tail] = List. % Head = 1, Tail = [2,3,4]
Кортеж — это фиксированное количество элементов, заключенных в фигурные скобки. Он часто используется для группировки связанных данных.
Пример:
Point = {point, 10, 20}.
{point, X, Y} = Point. % X = 10, Y = 20
Сопоставление с образцом (pattern matching) — это краеугольный камень Erlang, а не просто присваивание. Оператор `=` выполняет сопоставление, пытаясь сделать левую часть равной правой.
Пример:
{A, B} = {10, 20}. % Успех: A=10, B=20
{A, 20} = {30, 20}. % Успех: A=30
{A, B} = {1, 2, 3}. % Ошибка! Несоответствие паттернов
Функции определяются в модулях (modules). Модуль — это базовая единица кода. Одна функция может иметь несколько клауз (clauses), которые выбираются через сопоставление с образцом аргументов.
Пример модуля geometry.erl:
-module(geometry).
-export([area/1]).
area({rectangle, Width, Height}) -> Width * Height;
area({circle, Radius}) -> 3.14159 * Radius * Radius.
Компиляция: c(geometry). Вызов: geometry:area({rectangle, 10, 5}). % Вернет 50
Но истинная сила Erlang раскрывается в его конкурентной модели. Легковесные процессы (processes) — это изолированные единицы выполнения, не являющиеся потоками ОС. Их можно создавать десятки и даже сотни тысяч. Процессы общаются только через асинхронную передачу сообщений (message passing). Каждый процесс имеет свой почтовый ящик (mailbox).
Пример создания процесса и обмена сообщениями:
Pid = spawn(fun() -> receiver() end). % Создание процесса
Pid ! {self(), hello}. % Отправка сообщения {PID_отправителя, hello}
receiver() ->
receive
{From, Message} ->
io:format("Received ~p from ~p~n", [Message, From]);
Other ->
io:format("Unexpected: ~p~n", [Other])
end.
Управление ошибками в Erlang реализовано по философии «пусть он упадет» (let it crash). Вместо написания оборонительного кода, переполненного проверками, вы создаете деревья процессов-супервизоров (supervisors), которые следят за рабочими процессами (workers). Если рабочий процесс падает из-за непредвиденной ошибки, супервизор автоматически перезапускает его по заданной стратегии (например, один за одним, все вместе). Это позволяет изолировать сбои и создавать системы, которые самовосстанавливаются.
Пример простого супервизора:
-module(my_supervisor).
-behaviour(supervisor).
init([]) ->
SupFlags = #{strategy => one_for_one, intensity => 5, period => 10},
ChildSpec = #{
id => worker_id,
start => {my_worker, start_link, []},
restart => permanent,
shutdown => 2000
},
{ok, {SupFlags, [ChildSpec]}}.
Библиотека OTP (Open Telecom Platform) — это набор библиотек и паттернов, которые формализуют лучшие практики для создания устойчивых приложений. Ключевые поведения (behaviours): gen_server (универсальный сервер клиент-сервер), gen_statem (конечный автомат), supervisor (супервизор). Использование OTP избавляет от написания шаблонного кода и обеспечивает стандартизацию.
Пример простого gen_server:
-module(key_value_store).
-behaviour(gen_server).
% Клиентские функции
start_link() -> gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
put(Key, Value) -> gen_server:call(?MODULE, {put, Key, Value}).
get(Key) -> gen_server:call(?MODULE, {get, Key}).
% Колбэки сервера
init([]) -> {ok, #{}}.
handle_call({put, Key, Value}, _From, State) ->
NewState = maps:put(Key, Value, State),
{reply, ok, NewState};
handle_call({get, Key}, _From, State) ->
Reply = maps:get(Key, State, not_found),
{reply, Reply, State}.
Работа с бинарными данными и битовыми строками (bitstrings) в Erlang невероятно мощна и синтаксически элегантна, что идеально подходит для парсинга сетевых протоколов.
Пример:
= Data.
Этот один оператор извлекает 8-битный заголовок, 16-битную длину полезной нагрузки (в little-endian), затем саму полезную нагрузку указанной длины как бинарник, и остаток данных.
Изучение Erlang — это инвестиция в парадигму построения систем, где параллелизм, распределенность и отказоустойчивость являются не проблемами, а встроенными возможностями. Начните с основ синтаксиса и сопоставления с образцом, затем погрузитесь в модель процессов и обмен сообщениями, и, наконец, освойте мощь OTP. Это позволит вам создавать системы, которые не просто работают, а остаются в строю при любых обстоятельствах.
Полное руководство по Erlang: от основ синтаксиса до конкурентной модели с примерами кода
Исчерпывающее введение в язык Erlang и платформу OTP: от базового синтаксиса, сопоставления с образцом и структур данных до легковесных процессов, обмена сообщениями, супервизоров и примеров кода для gen_server.
216
3
Комментарии (8)