В середине 1980-х годов в компьютерной лаборатории компании Ericsson исследовали применимость существовавших на тот момент языков программирования для программного обеспечения телекоммуникационных систем. Джо Армстронг, Роберт Вирдинг (Robert Virding) и Майк Вильямс (Mike Williams) под руководством Бьярне Деккера (Bjarne Däcker), написав прототипы программ на различных языках, пришли к выводу, что ни один из этих языков не имел полного набора возможностей, необходимых в области телекоммуникационных систем. В результате был создан новый язык программирования — Erlang[8]. Своё название язык, вероятно, получил в честь датского математика и инженера Агнера Эрланга, основателя научного направления по изучению сетевого трафика в телекоммуникационных системах. По другой версии, название языка изначально было сокращением от «Ericsson language»[9].
Влияние на Erlang оказали ML, Миранда, Ада, Модула-2, CHILL, Пролог. Кроме того, на способ обновления программного обеспечения повлиял Smalltalk и использованные Ericsson проприетарные языки EriPascal и PLEX[8].
Потребовалось четыре года развития языка и прототипирования с использованием виртуальной машины Пролога, после чего в 1991 году Майк Вильямс переписал виртуальную машину для Erlang на Си. В 1992 году Erlang был впервые использован в коммерческом проекте[8]. В 1995 году вышла новая версия Erlang, вобравшая накопившийся к тому моменту опыт использования языка. Язык был сочтён достаточно развитым для использования в других продуктах компании (решения для широкополосной связи, GPRS, ATM)[8].
В декабре 1995 года случилось событие, которое Джо Армстронг считает решающим для Erlang: проект AXE-N в Ellemtel по созданию нового маршрутизатора (как оборудования, так и системного программного обеспечения на C++) потерпел неудачу. В результате реорганизации проекта удалось, использовав разработанное оборудование и язык программирования Erlang, начать работы над ATM-маршрутизаторами серии AXD. Ресурсов лаборатории для такого проекта оказалось недостаточно, поэтому для работ по Erlang было создано производственное подразделение под названием OTP (Open Telecom Platform)[10]. В 1996 году увидел свет одноимённый фреймворк OTP[8].
Неожиданно[11], в 1998 году топ-менеджмент Ericsson решил не брать на себя обязательств по разработке и поддержке собственного языка программирования, сосредоточившись вместо этого на Java. Использование Erlang было запрещено в новых проектах Ericsson Radio AB в связи с реализацией плана по аутсорсингу программной технологии компании Rational Inc.[12][13] Это решение очень сильно повлияло на будущее Erlang: оно привело к открытию кода Erlang под открытой лицензией EPL (аналог Mozilla Public License)[14], а также послужило главной причиной начала распространения языка за пределами создавшей его компании. Основным возражением против открытия исходного кода являлось решение вопросов, касающихся патентов, но эти трудности были преодолены[15]. Вскоре многие из основных разработчиков покинули Ericsson, чтобы организовать собственный бизнес — Bluetail AB[15][12].
В начале 2000-х годов научные круги стали проявлять интерес к Erlang. С 2002 года стал проводиться ежегодный Erlang Workshop. Ericsson продолжал спонсирование проекта HiPE (от англ.High-Performance Erlang — высокопроизводительный Erlang)[16]уппсальского университета[Примечание 1]. Проект HiPE занимался эффективной реализацией языка и инструментами для проверки типов, а с 2001 года созданный в группе проекта компилятор в машинный код входит в поставку свободно распространяемой версии Erlang/OTP[10]. Работы, связанные с Erlang, ведут и другие высшие учебные заведения. Инструменты для рефакторинга созданы в Кентском университете в Великобритании и университете Ло́ранда Э́твёша в Венгрии, инструменты для различных видов тестирования — в Мадридском политехническом университете, техническом университете Чалмерса и Гётеборгском университете[17].
Когда системы с симметричной многопроцессорностью только начинали завоёвывать рынок серверов и настольных компьютеров, бросая вызов разработчикам программного обеспечения, уже в 2006 году первая версия Erlang с поддержкой SMP была выпущена совместными усилиями команды OTP из Ericsson и команды HiPE[16]. Вскоре после этого вышла первая почти за десятилетие крупная монография по Erlang: «Programming Erlang» Джо Армстронга[18], после чего многие разработчики «открыли» для себя Erlang/OTP[16], и язык стал набирать популярность[19].
Процесс развития языка включает в себя рассмотрение предложений по развитию — EEP (англ.Erlang Enhancement Proposal). Через эти предложения Erlang-сообщество вносит изменения в стандартную поставку Erlang[20]. С внесёнными предложениями можно ознакомиться на веб-странице erlang.org/eeps[21].
По свидетельству Майка Вильямса, Erlang задумывался для решения трёх проблем разработки распределённыхсистем мягкого реального времени с высокой степенью параллелизма: возможности быстрой и эффективной разработки ПО; получения системы, устойчивой к программным и аппаратным сбоям, и возможности обновления системы «на лету», без простоя оборудования[22].
По словам Вильямса, философия, которой придерживались разработчики Erlang, подходит и для разработки программного обеспечения на этом языке[23]:
Найдите наиболее подходящие методы. Проектируйте с использованием прототипов;
Одних идей мало: нужно уметь их реализовать и знать, что они работают;
Делайте ошибки в небольшом масштабе, а не в производственном проекте.
Оригинальный текст (англ.)[показатьскрыть]
Find the right methods—Design by Prototyping.
It is not good enough to have ideas, you must also be able to implement them and know
they work.
Make mistakes on a small scale, not in a production project.
Большинство языков, созданных прежде Erlang, были разработаны без предварительного нахождения своей области применения, тогда как Erlang был разработан специально на основе требований к распределённым, отказоустойчивым, параллельным системам реального времени. С развитием сети Интернет оказалось, что многие приложения имеют аналогичные требования[14], чем и объясняется растущий интерес к языку[24].
Высокая отказоустойчивость кроется в применении изолированных друг от друга легковесных процессов, связанных лишь механизмом обмена сообщениями и сигналами выхода. Принцип разработчиков на Erlang по отношению к обработке ошибочных ситуаций в процессах[⇨] можно выразить в виде высказывания:
Позвольте приложению упасть, и пускай что-то другое имеет с этим дело.[25]
Оригинальный текст (англ.)[показатьскрыть]
Let it crash and let someone else deal with it
или сокращённо — «let it crash» («пусть падает»). Связано это с тем, что в Erlang-системе легко следить за завершением процесса, завершать процессы, связанные со сбойным, и запускать новые процессы[26].
Erlang является декларативным языком программирования, который скорее используется для описания того, что должно быть вычислено нежели как. Например, определение функции[⇨], которое использует сопоставление с образцом[⇨] для выбора одного из вариантов вычисления или извлечения элемента данных из составной структуры, напоминает уравнение. Сопоставление с образцом распространено даже на битовые строки[⇨], что упрощает реализацию телекоммуникационных протоколов[14].
Отличительной особенностью языка является применение легковесных процессов в соответствии с моделью акторов. Такой подход позволяет выполнять одновременно сотни тысяч и даже миллионы таких процессов, каждый из которых может иметь скромные требования по памяти[28]. Процессы изолированы друг от друга и не имеют общего состояния, но между ними можно установить связь и получать сообщения об их состоянии. Для взаимодействия процессов используется асинхронный обмен сообщениями. Каждый процесс имеет свою очередь сообщений, обработка которой использует сопоставление с образцом. Процесс, отправивший сообщение, не получает уведомления о доставке, даже если идентификатор процесса-получателя недействителен или получатель игнорирует сообщение. Таким образом, ответственность за правильно организованное взаимодействие между процессами лежит на разработчике[29].
Например, при реализации на Erlang сетевого чата структура программы может напрямую отражать одновременность действий пользователей по обмену сообщениями путём запуска новых процессов. Эффективность передачи сообщений сохраняется и при увеличении числа процессов, а требования к памяти минимизируются за счёт того, что легковесными процессами управляет виртуальная машина, а не средства нижележащей операционной системы[30].
Erlang с самого начала проектировался для распределённых вычислений и масштабируемости. Распределение вычислений встроено в синтаксис и семантику языка, поэтому построение системы можно вести, абстрагируясь от конкретного места вычислений. В стандартной поставке Erlang может наладить связь процессов по протоколу TCP/IP независимо от поддерживаемых им нижележащих платформ (операционных систем)[31].
Работающий экземпляр среды выполнения Erlang (англ.Erlang runtime system) называется узлом (англ.node). Программы, написанные на Erlang, способны работать на нескольких узлах. Узлами могут быть процессоры, многие ядра одного процессора, и даже целый кластер машин. Узел имеет имя и «знает» о существовании других узлов на данной машине или в сети. Создание и взаимодействие процессов разных узлов не отличается от организации взаимодействия процессов внутри узла. Для создания процесса на другом узле процессу достаточно знать его имя и, без особых на то оснований, он может не интересоваться физическим расположением взаимодействующего с ним процесса. Синтаксис отправки сообщения процессу на своём узле и удалённом — один и тот же[31].
Благодаря встроенным в язык возможностям распределённых вычислений объединение в кластер, балансировка нагрузки, добавление узлов и серверов, повышение надёжности вызывают лишь небольшие затраты на дополнительный код. По умолчанию узлы спроектированы для работы внутри обособленного сегмента сети (DMZ), но, если необходимо, коммуникация между узлами может происходить с применением защищённого криптографическими методами протокола SSL[31].
Программы на высокоуровневом языке Erlang могут быть использованы в системах мягкого реального времени (которое иногда переводят как «псевдореальное» или «квазиреальное»[32]). Автоматизированное управление памятью и сборка мусора действуют в рамках одного процесса, что даёт возможность создавать системы с миллисекундным временем отклика (даже несмотря на необходимость сборки мусора), не испытывающие ухудшения пропускной способности при высокой нагрузке[33].
Для систем, которые не могут быть остановлены для обновления кода, Erlang предлагает горячую замену кода (англ.hot code upgrade). При этом в приложении могут одновременно работать старая и новая версии кода. Таким способом программное обеспечение на Erlang может быть модернизировано без простоев, а выявленные ошибки исправлены[9][34].
Типизация в Erlang является сильной и динамической. Динамическая типизация была выбрана для языка Erlang по причине того, что первые разработчики были больше с ней знакомы[35]. По мнению Джо Армстронга, статическая типизация потребовала бы очень больших трудозатрат, в частности, реализовать систему горячей дозагрузки кода было бы крайне затруднительно[36]. Такая типизация, при которой возможные ошибки типов выявляются только во время выполнения, тем не менее, не помешала создавать системы с очень высоким уровнем доступности[35]. Данные в Erlang являются неизменяемыми: операции не переписывают старые значения, находящиеся в памяти. Если необходимо, модули на Erlang можно снабдить описаниями и определениями новых типов (не влияющими на компиляцию программы) для автоматической проверки типов с помощью утилиты Dialyzer[37].
В Erlang есть два типа числовых литералов: целые и с плавающей запятой, например: 125, 4.5e-20. Кроме обычной нотации, числа можно задавать через символ ASCII (например, $B означает 66) или вместе с указанием системы счисления с основанием от 2 до 36 (в старых версиях — до 16), например: 16#3f, 2#1010. В Erlang применяются целые числа произвольной точности и действительные числа двойной точности (64 бита), в стандарте IEEE 754—1985[38].
Для работы с числами можно использовать модуль math, который содержит обычный набор математических функций и функцию math:pi/0, возвращающую число [39]. Пример вычислений в интерактивной оболочке[⇨]:
Атом — константа с именем, которая должна быть заключена в одинарные кавычки, если не начинается со строчной буквы или содержит знаки, кроме букв, цифр, подчёркивания, точки и символа @. Понятие атома заимствовано из Пролога и его можно считать аналогом перечислений (enum) в других языках программирования (без необходимости предварительной декларации)[40]. Атомы используются почти исключительно в сравнениях, имеющих в Erlang очень эффективную реализацию[41]. Кроме того, некоторые атомы имеют определённый смысл в возвращаемых значениях и описании исключений. К ним относятся: error, ignore, noreply, ok, reply, stop, undefined[42].
Битовая строка используется для хранения в памяти нетипизированных данных. Строки, состоящие из целого количества октетов, называются бинарными (или двоичными) данными (англ.binaries). Синтаксис описания битовой строки довольно гибок, так как описывает значения битов отдельных диапазонов и может быть снабжён модификатором типа[43]. Несколько примеров в интерактивной командной оболочке:
В этом примере переменная X последовательно получает биты числа 2#111011, которые затем инвертируются операцией битового отрицания bnot (от англ.binary NOT), в результате чего получается число 4.
Кортеж (англ.tuple) — составной тип данных с фиксированным количеством элементов. При доступе к элементам кортежа с помощью встроенных функций нумерация элементов начинается с единицы, а не с нуля. Первый элемент кортежа принято использовать для указания роли кортежа в программе. Если первый элемент — атом, его называют тегом (англ.tag — «метка»). В Erlang принято строить различные типы данных на основе кортежей с тегами, что облегчает отладку программы и считается хорошим стилем программирования[45].
Для работы с кортежами есть несколько встроенных функций, например[45]:
Список (англ.list) — составной тип данных, содержащий переменное число элементов. Для манипуляции со списками можно применять функции модуля lists стандартной библиотеки[46]. Формально список определяется как имеющий голову (англ.head) и хвост (англ.tail), что выражается синтаксически в виде [HEAD|TAIL], где хвост обычно является списком (возможно, пустым). Пустой список обозначается [][47].
Списки можно записывать и более привычным способом. Следующие записи эквивалентны:
1>[a|[b|[c|[]]]].[a,b,c]
Для работы со списками можно применять списковые включения[48] (генераторы списков), например:
1>[X/2||X<-[1,2,3,4]].[0.5,1.0,1.5,2.0]
Модуль lists стандартной библиотеки[⇨] содержит функции для обработки списков (и строк, так как в Erlang строка является списком)[49], такие как нахождение максимума, сортировка, изменение порядка элементов на противоположный, суммирование элементов и т. п. В следующем примере два списка склеиваются операцией конкатенации, а затем разбиваются на две части функцией lists:split/2:
В модуле lists имеется также набор функций высшего порядка, таких как lists:all/2, lists:any/2, lists:dropwhile/2, lists:filter/2, lists:foldl/3, lists:foldr/3, lists:map/2, lists:foreach/2. Следующий пример иллюстрирует работу функции lists:foldr (англ.fold — свернуть, «r» от англ.right — правая) для свёртки списка, первым параметром которой должна быть функция:
1>D=fun(V,A)->V/Aend.% функция D - деление V на A#Fun<erl_eval.12.82930912>2>lists:foldr(D,1,[1,2,4,8]).0.253>1/(2/(4/(8/1))).0.25
Результат выполнения свёртки справа налево (в строке 2) тождественен цепочечному делению (строка 3). Второй параметр foldr даёт начальное значение для так называемого аккумулятора. Для каждого элемента списка (справа налево) к элементу и аккумулятору применяется функция, заданная первым аргументом foldr, а значение записывается в аккумулятор. По исчерпанию списка функция возвращает значение аккумулятора[50]. Функция является достаточно мощным средством, если учесть, что аккумулятор может быть списком или кортежем[51].
В Erlang нет самостоятельного типа для строк: внутренне строки представляются списками. Синтаксически строку можно задать кавычками. Так, "Привет!" равносилен (в подходящей кодировке) списку [1055,1088,1080,1074,1077,1090,33]. Erlang поддерживает Unicode как в строке, так и в записи отдельного знака (через $)[52].
Атомы и строки внешне достаточно похожи, но имеют совершенно различные реализации. Тогда как атомы можно только сравнивать, строки поддерживают многие другие операции, для них есть множество функций в модулях lists и string. Строка может выполнять функции атома, но память, занимаемая строкой пропорциональна её длине, тогда как атомы хранятся в системной таблице и на каждое использование атома в программе приходится лишь пара байтов, вне зависимости от длины атома. Сравнение двух атомов — это сравнение двух внутренних идентификаторов, выполняемое за одну операцию, тогда как сравнение строк предполагает поэлементный проход элементов строк[53].
Для значений истина и ложь в Erlang применяются атомы true (истина) и false (ложь), которые и используются операциями сравнения, логическими операциями, встроенными функциями[54]. Пример:
Fun-выражение позволяет создать анонимную функцию, например, для передачи в качестве параметра другим функциям. С помощью fun можно также получить функциональный объект для функции из модуля[55]. Примеры:
Чтобы помечать отдельные элементы кортежей и избежать ошибок при написании программы, в Erlang был внесён синтаксис записей (англ.record). Для работы с записями, необходимо в начале дать описание записи директивой -record, например, для записи user описание может быть следующим[56]:
-record(user,{login="anon",password,nick}).
Из этого описания компилятор узнаёт, что имеются в виду кортежи из четырёх элементов, в которых элементы со второго по четвёртый соответствуют полям login, password, nick (порядок важен) записи с именем user (определяется атомом в первом элементе кортежа). Значением по умолчанию для поля login является строка "anon". Если значение по умолчанию не указано явно, подразумевается атом undefined.
Создание записей и извлечение элементов записи всегда требует явного указания имени записи[56]:
R0=#user{}% все поля получают значения по умолчаниюR1=#user{login="user1",password="secret",nick="john"}% все поля получили значения
Синтаксис доступа к значениям полей записи: R1#user.login, R0#user.nick[56].
В языке Erlang имеются и другие типы данных. Тип ссылка (англ.reference) является практически уникальной в среде времени выполнения Erlang[57]. Ссылка создаётся вызовом функции make_ref/0 и может повториться через 282 вызовов этой функции[58]. Ссылки можно сравнивать на равенство, а применяются они для одноразовых пометок или «волшебного печенья»[59].
Идентификатор порта (англ.port identifier) определяет порт для связи с внешним по отношению к Erlang-системе миром. Порт позволяет создавшему его процессу-владельцу (так называемому присоединённому процессу) обмениваться бинарными сообщениями со сторонними программами и ОС способом, принятым в данной операционной системе[59][60][41].
Идентификатор процесса (англ.Pid), как и следует из его названия, идентифицирует процесс, порождаемый различными функциями spawn. Идентификатор можно считать уникальным во время работы Erlang-системы, но в долго работающих системах могут всё-таки быть использованы повторно, что обычно не является проблемой на практике[59].
Для преобразования типов используются встроенные функции (BIF, от англ.builtin function) вида x_to_y («из x в y»), а для проверки принадлежности значения тому или иному типу — функции вида is_x («является x»):
Все эти операции левоассоциативны. Унарные операции имеют наивысший приоритет, затем следует умножение и деление, наименьший приоритет у сложения и вычитания. При необходимости целое может приводиться к типу с плавающей запятой[61].
Логические операции работают над логическими значениями true (истина) и false (ложь), получаемыми в результате сравнений и применения функций проверки типа[63].
Операции сравнения получают два операнда, а результатом операции является логическое значение true или false. В Erlang есть следующие операции: == (равно), /= (не равно), =< (меньше или равно), < (меньше), > (больше), >= (больше или равно), а также сравнения, которые работают без приведения к одному типу: =/= (не равно в точности) и =:= (равно в точности).
Можно сравнивать и значения разных типов, но они считаются в Erlang упорядоченными следующим образом[64]:
число < атом < ссылка < функция < порт < идентификатор процесса < кортеж < список < бинарные данные
Списки считаются упорядоченными в лексикографическом порядке, а кортежи сравниваются по длине, и только затем в лексикографическом порядке[64].
Переменные служат для хранения значений простых и составных типов. Имя переменной начинается с прописной буквы (в специальных случаях — с подчёркивания) и может содержать буквы, цифры, подчёркивания. Значение можно присвоить переменной лишь один раз — это свойство языка программирования называется единичным присваиванием (англ.single assignment)[65]. К достоинствам единичного присваивания можно отнести устранение необходимости в блокировках, а также упрощение отладки программы[66].
Передача параметров в функцию происходит по значению, поэтому все они вычисляются перед вызовом функции[65].
Область видимости переменной распространяется от момента её появления в заголовочной части описания функции или присваивания до конца части описания функции. Пример[67]:
В этом примере область видимости X — всё описание функции binomial/1, а Y — от присваивания до конца описания. Переменная Y во второй части (клоза) описания функции prod/1 не имеет отношения к переменной Y из binomial/1: её область видимости распространяется до конца этого клоза.
При выходе вычислений за пределы области видимости переменной память, занятая её содержимым, может быть освобождена в процессе сборки мусора, если значение переменной не используется в другой части программы[68].
Сопоставление с образцом используется в Erlang для присваивания (в том числе, при работе с параметрами функций), управления потоком выполнения программы, извлечения значений составных типов, выбора сообщения из очереди. В левой части сравнения (или в заголовке функции) могут находиться связанные (уже имеющие значение) и несвязанные (получающие значение) переменные, а также литералы (атомы, числа, строки). В результате исполнения сравнение может оказаться успешным (в этом случае переменные связываются со значениями) и неуспешным — переменные остаются несвязанными. В образце могут быть переменные, значение которых для образца безразлично: их имена записываются начинающимися с подчёркивания[69]. Переменная с именем _ (подчёркивание) сопоставляется с любым значением, но при этом не происходит связывания. Такую переменную можно применять много раз.
Программы на Erlang состоят из функций, которые вызывают друг друга. Количество параметров функции называется арностью. При вызове функции заголовочные части описания функции сопоставляются с образцом. В случае совпадения параметров вызова, формальные параметры связываются с фактическими и исполняется соответствующая часть тела функции[70]. Запись варианта вычисления функции для некоторого образца может называется клозом от англ.clause, а определение функции — это набор из одного или более клозов[71].
Для уточнения сопоставления с образцом в функциях можно использовать охранные выражения, которые следуют после ключевого слова when[72]. В примере ниже определена функция вычисления знака числа, которая рассчитывается в зависимости от сравнения параметра с нулём:
Клозы Erlang перебирает в том порядке, в котором они записаны, пока не будет найден подходящий заголовок[73]. В охранных выражениях можно использовать только ограниченный набор встроенных функций, так как эти функции не должны иметь побочных эффектов.
Разумеется, функции Erlang поддерживают рекурсивные вызовы. В случае, когда определение функции оканчивается рекурсивным вызовом (хвостовая рекурсия), Erlang использует оптимизацию: стек вызовов не применяется[74].
Как параметром, так и результатом функции может быть другая функция. В следующем примере функция одного аргумента возвращает функцию для прибавления аргумента[75]:
1>Plus=fun(X)->fun(Y)->X+Yendend.% Определение функции, возвращающей функцию#Fun<erl_eval.6.82930912>2>Plus(2).% Функция возвращает Fun-объект #Fun<erl_eval.6.82930912>3>Plus(2)(3).% Такой синтаксис не работает*1:syntaxerrorbefore:'('4>(Plus(2))(3).% Дополнительные скобки позволяют добиться требуемого результата55>Plus2=Plus(2),Plus2(3).% То же самое с использованием дополнительной переменной5
-module(qsort).-export([qsort/1]).qsort([])->[];% Тривиальный случай пустого спискаqsort([Pivot|Rest])->% Конкатенация списка элементов до Pivot, списка из одного элемента Pivot и после Pivotqsort([Front||Front<-Rest,Front<Pivot])++[Pivot]++qsort([Back||Back<-Rest,Back>=Pivot]).
В этом примере функция qsort вызывается рекурсивно до исчерпания всех элементов. Выражение [Front || Front <- Rest, Front < Pivot] собирает список Front из элементов Rest таких, что элемент Front меньше Pivot. Оператор ++склеивает списки.
Кроме выбора описания в определении функции, в Erlang есть и другие условные выражения: case-выражения (выражение выбора) и if-выражения. Выражение выбора позволяет организовать сопоставление с образцом внутри функции и обычно имеет следующий синтаксис:
Это выражение всегда возвращает значение, соответствующее последнему вычисленному выражению в строке с подошедшим образцом. Это возвращаемое значение может служить возвращаемым значением функции, а может быть присвоено переменной[76]. Как и в заголовочной части функции, после образца может следовать охранное выражение.
Упрощённым вариантом case-выражения является if-выражение:
Здесь охранаi — охранное выражение. Первое истинное охранное выражение вызывает выполнение соответствующих выражений, последнее из которых и является значением всего if-выражение[77]. Следует заметить, что и здесь в охранном выражении можно применять только ограниченный набор операций и встроенных функций.
Запятые в охранном выражении работают как операция and, например[78]:
ifX=<0->'меньше или равно нулю';X>0,X<10->'больше нуля и меньше десяти';X>=10->'больше или равно десяти';end
Компилятор Erlang следит за безопасностью связывания переменных внутри условных выражений, как видно из следующего примера модуля:
Препроцессор Erlang (EPP) позволяет вкладывать файлы с исходным кодом один в другой, определять макросы и осуществлять простые и параметризованные макроподстановки[80]. Макрос определяется с помощью директивы -define, а макроподстановка осуществляется указанием имени макроса и возможных параметров после вопросительного знака (?). Следующий пример показывает определение и применение параметризованного макроса:
Имя макроса обычно пишется прописными буквами. Определение макроса должно содержать лексемы Erlang целиком (например, попытка задать часть имени переменной с помощью макроса вызовет синтаксическую ошибку). Макросы могут использоваться для повышения удобочитаемости кода в охранных выражениях, для операторов отладки и т. п.[81] Препроцессор имеет несколько предопределённых макросов, которые нельзя переопределить: ?MODULE, ?MODULE_STRING, ?FILE, ?LINE, ?MACHINE[82].
Заголовочный файл (расширение .hrl) с определениями макросов и записей можно включить при помощи директивы -include[83].
Как и в случае case-выражения, вычисляемое выражение сопоставляется с образцом (части между of и catch) для получения результата[Примечание 2][85]. После ключевого слова catch следуют части обработки исключений, в которых в дополнение к образцам исключений могут быть указаны классы исключений (перед двоеточием): error, throw и exit. Подчёркивание может использоваться как в образце, так и в классе исключения[86]. Следующий простой пример иллюстрирует перехват ошибки класса error при вычислении квадратного корня:
Для создания исключений, определённых пользователем, используется функция throw/1, которая принимает кортеж с более детальным описанием возникшей ошибки[87] и генерирует исключение класса throw. Использование этой функции нежелательно из-за ухудшения удобочитаемости кода программы, но может потребоваться в некоторых случаях при работе с вложенными структурами данных, например, при разборе XML[86]. Исключения класса exit возникают в результате вызова встроенной функции exit/1 или сигнала выхода[86].
До разработки Ричардом Карлссоном (Richard Carlsson) из команды проекта HiPE описанного выше нового механизма обработки исключений (появился в версии R10B) в Erlang использовались catch-выражения[88].
Код программы на Erlang можно разбить на отдельные модули. Модуль — имя для набора функций, организованных в одном файле. Имя модуля должно совпадать с именем файла (если отбросить расширение)[89]. Модуль можно откомпилировать в байт-код как из командной строки операционной системы, так и из командной оболочки Erlang[90]. В файле модуля можно записать объявления функций и директивы (иногда называются атрибутами)[91]. Обязательным атрибутом является только -module(атом_имени_модуля). Другой часто используемый атрибут — -export — применяется для указания списка экспортируемых функций, то есть функций, которые можно использовать за пределами модуля.
Функции в Erlang однозначно определяются модулем, именем и арностью. Например, math:cos/1 соответствует функции cos из модуля math, принимающей один аргумент. Вызвать функцию можно так: math:cos(1.2)[89].
Исходный текст модуля компилируется в BEAM-файл — файл, содержащий байт-код виртуальной машины BEAM (англ.Bogdan’s/Björn's Erlang Abstract Machine[90]). В свою очередь, ERTS (англ.Erlang Runtime System — система времени выполнения Erlang) выполняет этот код[55].
Основной абстракцией параллельного программирования в Erlang является процесс. Процессы могут порождать другие процессы, выполняться одновременно, обмениваться сообщениями, реагировать на завершение друг друга.
Для создания нового процесса служит несколько встроенных функций (spawn и её аналоги)[92]. Функции возвращают идентификатор процесса, который может использоваться, например, для отправки сообщений вновь созданному процессу. В интерактивной консоли erl можно получить список процессов и другую информацию посредством вызова функций processes(). и i(). соответственно[93].
Как и язык Оккам, Erlang использует для отправки сообщения синтаксис с восклицательным знаком: ИдПроцесса ! Сообщение. Приём сообщения — то есть извлечение его из очереди («почтового ящика») процесса — выполняется с помощью receive-выражений, обычно записываемых следующим образом[94]:
Встретив такое выражение, интерпретатор последовательно просматривает сообщения из очереди. Каждое сообщение интерпретатор сопоставляет с образцом и, если оно удовлетворяет образцу, вычисляются соответствующие выражения. Когда все сообщения перебраны, и подходящего не оказалось, процесс блокируется в ожидании новых сообщений, после чего перебор очереди повторяется. Если в receive-выражении отсутствует образец, которому удовлетворяет любое сообщение, такое выражение называется выборочным receive-выражением[93].
Процесс можно связать с другим, в результате чего между процессами устанавливается двунаправленное соединение (англ.link). В случае, если один из процессов завершается ненормально, всем связанным с ним процессам передаётся сигнал выхода (англ.exit signal). Процессы, получившие сигнал, завершаются, распространяя сигнал дальше[95][96]. Сигнал выхода является кортежем, элементами которого являются атом 'EXIT' (выход), идентификатор завершившегося процесса и причину завершения процесса. Причина завершения передаётся по цепочке завершающихся процессов[97].
Процесс может осуществить перехват ошибки (англ.trapping errors), если у него установлен флаг перехвата выхода[Примечание 3]. Такой процесс получает сигналы выхода связанных с ним процессов в виде обычных сообщений с той же структурой кортежа. Перехваченный сигнала выхода более не передаётся связанным с процессом-перехватчиком процессам[98]. Сигнал выхода с причиной — атомом normal (нормальное завершение процесса) не вызывает завершения связанного процесса. Если же причина — атом kill, процесс завершается безусловно (независимо от флага перехвата выхода), а связанным с ним процессам в качестве причины отправляется атом killed, что даёт им возможность среагировать[99].
В Erlang есть возможность установить и однонаправленное соединение. При завершении наблюдаемого процесса процесс-наблюдатель получает сообщение с указанием причины завершения[100].
Процесс может остановить сам себя или другой процесс, вызвав функцию exit[101].
В планировщике процессов Erlang-системы проблема ввода-вывода, присущая многим другим языкам параллельного программирования, решена достаточно элегантно. Управление вводом-выводом, интегрированное с планировщиком, уже на самом нижнем уровне осуществляется на основе событий, что позволяет программе обрабатывать входящие и исходящие данные без излишних блокировок. Такой подход требует меньшего числа установки и разрыва соединений, а также убирает необходимость в блокировках и переключениях контекста. К сожалению, такой достаточно эффективный способ более сложен для понимания программистами, и находит применение в основном в системах с явными требования по высокой доступности и низкому времени отклика. Реализация событийно-ориентированного ввода-вывода встроена в Erlang-систему, что является ещё одним преимуществом при проектировании параллельных приложений[102].
Стандартная библиотека содержит модуль io с функциями ввода-вывода. Такие функции содержат побочные эффекты, заключающиеся в появлении выведенной информации на консоли или записывании данных в файл на диске. Например, функция io:format для форматированного вывода выводит строку с подстановкой параметров, возвращая в случае успеха атом ok[103]:
Функции модуля io включают в себя стандартный серверный интерфейс ввода-вывода. Протокол ввода-вывода Erlang (англ.The Erlang I/O-protocol) детально определяет связь клиента и сервера. Под сервером ввода-вывода понимается процесс, который обрабатывает запросы и выполняет запрошенные команды, например, на устройстве ввода-вывода. Клиентом является любой Erlang-процесс, которому требуется работать с устройством[104][105].
Согласно официальной документации, стандартная библиотека модулей STDLIB[106] является обязательной для включения в минимальную систему Erlang/OTP[107] наряду с ядром Erlang. В библиотеку входят модули, предоставляющие разнообразные функции для работы со встроенными типами и другими структурами данных, ввода-вывода, обращения к среде окружения, для работы с файловой системой, процессами и т. п.
Модуль array определяет (функциональный) абстрактный тип данных для динамического массива и имеет функции, позволяющие извлекать и обновлять элементы массива, определять рекурсивные функции для работы с массивами. Модуль string расширяет возможности модуля lists функциями для работы конкретно со списками символов, какими являются строки в Erlang. Модуль dict (от англ.dictionary — словарь) содержит функции для ассоциативного массива, позволяющие хранить, извлекать и удалять значения по ключу, соединять массивы и производить итерации по элементам. Математические функции можно найти в модуле math, а функции для генерации псевдослучайных чисел содержатся в модуле random. Модуль calendar предоставляет функции для григорианского календаря: запросы текущей даты, преобразования единиц измерения и интервалов времени, а модуль timer содержит функции перевода интервалов времени к миллисекундам, запуска событий по таймеру и другие, связанные со временем, функции. Модуль erlang содержит все встроенные функции Erlang, как общие, так и относящиеся к виртуальной машине. Модуль file даёт доступ к функциям файловой системы, таким как открытие, чтение, запись, удаление файлов, а модуль filename позволяет писать функции для манипуляции с именами и путями к файлам, абстрагируясь от конкретной операционной системы. Модуль io предоставляет функции ввода-вывода. Кроме этих наиболее важных модулей, стандартная библиотека содержит и многие другие, с которыми можно познакомиться по документации[108].
Для организации коллекций в оперативной памяти Erlang предлагает модуль ets (ETS, англ.Erlang Term Storage — «хранилище термов Erlang»[Примечание 4]). ETS может хранить четыре вида коллекций: множество (англ.set), упорядоченное множество (англ.ordered set), мультимножество (англ.bag), мультимножество с повторениями (англ.duplicate bag)[109]. Доступ к элементам коллекций происходит по ключевому полю кортежа (ключи могут быть любых типов). Упорядоченные множества реализованы в виде бинарных сбалансированных АВЛ-деревьев, а остальные коллекции — с использованием хеш-таблиц[110].
DETS-таблицы дополняют функциональность ETS-таблиц (за исключением упорядоченных множеств), позволяя сохранять данные в файлах[111].
OTP (англ.Open Telecom Platform) является хорошо отлаженным набором полезных поведений (англ.behaviours) процессов и используется для создания серверных приложений. OTP формализует действия процессов и позволяет строить на их основе OTP-приложения (не следует путать с приложением — готовым программным продуктом). В модулях ОТР определены общие, стандартизированные шаблоны для конструирования параллельных приложений[112]. Наиболее популярными поведениями являются обобщённый сервер и наблюдатель (англ.supervisor), но имеются и другие: конечный автомат, обработчик событий[113]. OTP содержит и другое связующее программное обеспечение (англ.middleware), например, СУБДMnesia.
OTP-поведения делятся на рабочие процессы (англ.worker processes), выполняющие собственно обработку запросов, и процессов-наблюдателей (англ.supervisors). В задачу последних входит слежение за рабочими процессами и другими процессами-наблюдателями — потомками. Деревья наблюдателей составляют OTP-приложение (англ.application)[114]. Документация по Erlang определяет OTP-приложение как компонент, реализующий некоторую функциональность, которая может быть независимо запущена на исполнение и остановлена как целое, а также повторно использована в других системах[115]. Разработчик приложения пишет код модулей функций обратного вызова (англ.call-back module), в которых и находится специфичная для данного приложения часть функциональности[114].
Хотя OTP строго говоря не является частью языка Erlang, он настолько вошёл в культуру и практику разработчиков на Erlang, что подчас между ними сложно провести границу[113].
Разработка приложений с графическим интерфейсом пользователя (не считая веб-интерфейсов) может вестись при помощи библиотеки wxErlang — библиотеки wxWidgets, портированной для Erlang. WxErlang входит в стандартную поставку Erlang/OTP. WxWidgets написан на C++, поэтому перед разработчиками wxErlang стояла задача выразить средствами Erlang иерархию объектов. Несколько упрощая, в wxErlang классам соответствуют модули, а объектам — ссылки. Макросам на C++ соответствуют макросы Erlang. Некоторые типы данных, для которых в C++ были использованы классы, представляются в Erlang с помощью других типов данных, например, wxPoint задаётся в виде кортежа из двух элементов. События в wxErlang могут быть обработаны в Erlang либо с помощью функций обратного вызова (англ.call-back functions), либо более естественной в среде Erlang передачей сообщений[116].
Интерактивная оболочка (англ.shell) для Erlang может быть вызвана в Unix-подобных системах по команде erl, в Windows — werl[117]. В оболочке можно вводить выражения и получать результат их выполнения, опробовать новый код, заниматься интерактивной отладкой, а также управлять системой, находящейся в промышленной эксплуатации[118].
В оболочке можно использовать дополнительные функции («команды»), доступные только в ней. Например, команда q(). осуществляет выход из оболочки с завершением всего, что делает Erlang-система[118].
В оболочке можно вызвать BREAK-меню с помощью Ctrl+C (в Unix-подобных ОС) или Ctrl+Break (в Windows). В этом меню есть различные команды, в том числе a — немедленный останов, c — продолжение работы в оболочке и другие информационные и вспомогательные команды для работы с Erlang-системой[118]. Комбинацией клавиш Ctrl+G вызывается ещё одно командное меню, с помощью которого можно, среди прочего, остановить «завесивший» оболочку процесс и вернуться в оболочку (i и затем c)[119].
Текст от знака процента (%) до конца строки считается комментарием в Erlang. Генерация документации из исходного кода в Erlang может производиться системой документирования EDoc. Для документирования кода модуля достаточно добавить определённым образом размеченный текст, а также файл overview.edoc для документации уровня проекта (в последнем необязательно использовать знаки комментария)[120]. Инструменты для работы с кодом на Erlang, например, erlang-режим в Emacs, подразумевают некоторые соглашения по употреблению символов комментария. Так, утроенный знак процента вызывает выравнивание по левому краю, удвоенный — выравнивание на уровне окружающего кода, а одиночный знак процента используется для обозначения комментария после кода, в конце строки[121]. Разработчики Erlang выработали определённые стилевые соглашения, касающиеся организации и оформления исходного кода. Например, хорошим стилем считается понижение вложенности синтаксических структур, написание коротких модулей (менее 400 строк кода) и функций (не длиннее 15-20 строк кода), использование осмысленных имён для переменных и функций и т. п.[122][123]
Приложение Dialyzer, разработанное в рамках проекта HiPE и входящее в стандартную поставку, позволяет выявить ошибки (в том числе ошибки типизации) путём статического анализа кода. Программа TypEr, написанная Тобиасом Линдалом (Tobias Lindahl) и Костисом Сагонасом (Kostis Sagonas), является частью Dialyzer. Эта программа позволяет проверять определения типов функций, сверять указанный в директиве -spec тип функции с её определением, выполнить вывод типов[124]. Программа TypEr выводит все типы, соответствующие успешному применению функции, в общем случае — лишь приблизительно, в более грубую сторону. Использование функции любым другим способом обязательно приведёт к ошибке времени исполнения[124]. В следующем примере показан синтаксис определения типа (директива -type), объявление типа полей записи и директива -spec вместе с определением функции:
-type(user_status()::disabled|enabled).% статус - один из двух атомов-record(user,{login="anon"::string(),% типы полей записиpassword::string(),status::user_status(),nickname::string()}).-spec(check_password(string(),#user{})->ok|{error,string()}).% объявление функцииcheck_password(Password,User)->% определение функции...
Dialyzer (от англ.DIscrepancy AnaLYZer for ERlang Programs — «анализатор противоречий для Erlang-программ») выявляет в коде отдельных модулей и целых приложений избыточные проверки, ошибки типов, недостижимый код. Все выявленные инструментом дефекты требуют устранения, так как инструмент не даёт ложных срабатываний. Для каждой функции всех проверяемых модулей Dialyzer устанавливает тип, используя основанный на ограничениях вывод типов и анализ потоков данных. После определения типов функций производится анализ противоречий в программе[125].
Erlang предоставляет EUnit для модульного тестирования и фреймворк Common Test для системного тестирования. EUnit содержит средства для описания тестов, включая необходимый для этого набор макросов, а также производит вывод отчёта по окончании тестирования. Тестирование модулей происходит путём подключения заголовочного файла из EUnit, а функции с тестами могут быть как включены в сам тестируемый модуль, а так и вынесены в отдельный[126].
Тестирование параллельных программ можно выполнить с помощью Quviq Quick Check (версия Mini этого продукта доступна бесплатно)[127]. Кроме тестирования, можно провести проверку всех возможных вариантов исходных данных с помощью метода проверки моделей. Для этого можно воспользоваться созданной в Мадридском политехническом университете отдельно распространяемой утилитой McErlang[128].
Для Erlang разработаны несколько инструментов рефакторинга исходного кода, такие как RefactorErl, Wrangler, а также автоматическая, независимая от IDE утилита tidier. Утилита tidier позволяет автоматически находить и производить эквивалентные преобразование кода, например, заменяет
Как и многие другие языки программирования, Erlang имеет свои секреты написания эффективного кода. Совершенствование языка делает некоторые из трюков устаревшими, поэтому документация является лучшим руководством в вопросах оптимизации, в совокупности с профилированием и стресс-тестированием.
Например, при работе со списками не рекомендуется добавлять элемент в конец длинного списка с помощью конкатенации или функции добавления элемента к списку. Вместо этого сто́ит рассмотреть возможность добавления элемента в начало списка, а конечный результат обработать функцией обращения порядка элементов списка[131].
Свои рекомендации есть и для увеличения эффективности параллельных программ. Например, действия, требующие много памяти, лучше всего выделять в отдельный процесс, так как при этом затраты на сборку мусора будут минимальны: память будет освобождена по завершении процесса[132].
Erlang-система позволяет выполнять интеграцию с системами на других языках программирования. Имеются механизмы для сетевого взаимодействия с Си, Java, Лисп, Perl, Python, Ruby. Например, для более эффективного синхронного вызова небольших функций на Си можно использовать платформно-зависимые функции (англ.NIF, natively implemented function). Высокоуровневые библиотеки позволяют Erlang-системе представлять С или Java-узлы как обычные Erlang-узлы. Другие языки могут быть более тесно сопряжены со средой выполнения Erlang с помощью драйверов или сетевых сокетов посредством протоколов вроде HTTP, SNMP, IIOP[133]. Например, Ruby может взаимодействовать с Erlang посредством пакета erlectricity, а для Python разработана реализация Erlang-узла в виде пакета py-interface[134].
Виртуальная машина Erlang находит применение и в других языках программирования, например, Elixir[135][136] и проекте Erl2 Джо Армстронга[137][136]. Кроме того, Роберт Вирдинг поддерживает проект Lisp Flavored Erlang («Erlang, приправленный Лиспом»), в котором синтаксис Лиспа используется с компилятором Erlang[138]. Есть и другие BEAM-языки: Efene, Joxa, Reia[139], Luerl, Erlog[140].
Официальный сайт упоминает[141] проект Erjang[142], в котором используется виртуальная машина Java.
Хотя опытные Erlang-программисты давно заметили, что их программы для тех же задач получаются более краткими по сравнению с другими широко используемыми в промышленности языками программирования, эмпирическое исследование показало, что для изученных телекоммуникационных приложений код на Erlang был на 70—85 % короче, чем на C++, а производительность системы при переписывании кода с C++ на Erlang возросла почти на 100 %[143][144]. Для одного из использованных в исследовании проектов разница была объяснена написанием дополнительного C++-кода в рамках защитного программирования, управления памятью и кода для высокоуровневой коммуникации, то есть возможностями, которые являются частью языка Erlang и библиотек OTP[144].
Влияние теории взаимодействующих последовательных процессовЧарльза Э. Хоара чувствуется как в Go, так и в Erlang. В Erlang процессы, в соответствии с моделью акторов, отправляют сообщения друг другу напрямую. В Go то же самое происходит посредством каналов (англ.channels)[145]. Другим отличием является то, что каналы в Go имеют типы. В Erlang же нет типизации времени компиляции за исключением охранных выражений, что позволяет посылать процессам сообщения любого типа, но «непонятое» сообщение либо будет проигнорировано, либо навсегда останется в очереди[145]. Go позволяет легко организовать группу «go-программ» (англ.goroutine — намёк на англ.co-routine — сопрограмма) для получения сообщений из некоторого канала (такой подход известен как пул потоков). В Erlang, при проектировании которого уделялось особое внимание детерминизму и времени задержки (англ.latency), реализация рабочего пула возможна, но требует дополнительных усилий. Множественные отправители тривиально реализуются в обоих языках[145]. Erlang-процесс может послать сообщение и ждать на него ответ (соответствующий некоторому образцу), игнорируя другие сообщения в очереди. В Go такое невозможно, но подобная функциональность может быть достигнута созданием (в том числе, динамическим) новых вводов, то есть разделением каналов по назначению[145]. Go требует явного указания того, какие go-программы будут взаимодействовать с другими передачей сообщений, тогда как в Erlang отсутствует разделяемое между процессами изменяемое состояние (англ.shared mutable state) и поэтому изолированный процесс очень редко представляет интерес[145].
Абстракции взаимодействующих процессов достаточно похожи в Erlang и Go, однако во избежание ошибок при переходе с одного языка на другой следует учитывать нюансы: шаблоны, которые хороши в одном языке, могут не подходить для другого[145].
Как и любой язык программирования, Erlang не свободен от недостатков[146]. К погрешностям синтаксиса можно отнести зависимость от символа окончания выражения от контекста (это может быть ., , или ;), что требует дополнительного внимания при перемене выражений местами, излишнюю многословность записей (тип записи приходится упоминать при каждом доступе к члену записи), необходимость полного перечисления альтернатив в if-выражении во избежание выбрасывания исключения, если ни одно из условий не выполнено. К недостаткам можно отнести строго ограниченный набор функций, которые можно использовать в if-выражениях (этот недостаток можно обойти использованием case-выражений). Функциональный стиль и неизменяемые переменные приводят в некоторых приложениях (например, тесты) к бо́льшему количеству правок, чем в других языках программирования, так как вставка некоторой промежуточной обработки может потребовать новых имён переменных, что может привести к изменениям в коде, следующем далее по тексту. Из недостатков системы типов можно указать отсутствие строкового типа, а также невозможность динамически добавлять в записи новые члены. Есть проблемы и с организацией исходного кода, которая возможна только через создание нового файла, а также отсутствие пространств имён, классов или других средств для организации кода. Уровень качества модулей, за исключением основных, и документации оставляет желать лучшего[146].
Один из создателей языка, Джо Армстронг, в своём выступлении на конференции по истории языков программирования в 2007 году перечислил список областей, в которых Erlang можно было бы улучшить[147]:
Использование сборки мусора для атомов.
Улучшение средств сопряжения со внешним кодом (англ.foreign code).
Усиление изоляции между процессами.
Более избирательная система безопасности среди узлов Erlang, основанная на различной степени доверия.
Массовое распространение Erlang может сдерживать необычный для большинства программистов синтаксис, использование функциональной парадигмы, а также то, что наилучшая на 2010 год реализация языка использует виртуальную машину BEAM, а не более распространённую JVM[148].
Типичная архитектура системы, использующей Erlang/OTP. Приложения Erlang пользуются службами Mnesia, SASL, агентами SNMP-мониторинга и другими на базе фреймворка OTP, который в свою очередь использует ERTS. Программы других систем программирования поддерживаются в меньшей степени[149].
В силу своих особенностей Erlang и существующих библиотек модулей Erlang подходит для создания сетевых серверов, распределённых систем, программ с GUI и подобных им интерактивных программ, инструментов для тестирования, управления и слежения, в общем, приложений с нерегулярным параллелизмом, в которых распараллеливаемые задачи достаточно разнообразны. Erlang не особенно хорош для написания кода, содержащего интенсивные вычисления с плавающей запятой, требующего включения нативного кода конкретной платформы или сильной оптимизации, а также для создания приложений, требующих синхронного параллельного выполнения задач. Не подходит Erlang и для проектов, в которых код должен исполняться на JVM или CLR, или проектов, требующих множества библиотек из других систем программирования[150].
Можно сказать, что Erlang стал применяться для разработки облачных систем ещё до того, как сформировалось само понятие облачных вычислений[151]. Язык Erlang используется в масштабных телекоммуникационных и Интернет-приложениях многими компаниями, включая Amazon EC2 с реализацией SimpleDB, сервис социальных закладок Delicious, Facebook (бэкенд для чата), T-Mobile (сервис SMS и системы аутентификации)[152]. Серверное программное обеспечение WhatsApp написано на Erlang. В январе 2012 года серверы WhatsApp под FreeBSD с 96 ГБ оперативной памяти смогли обрабатывать от 1 до 2,8 миллионов соединений[153][154].
Erlang часто ставят в заслугу легендарную надёжность ATM-коммутатора AXD301 (полтора миллиона строк кода на Erlang, полмиллиона — на C/C++) в сети British Telecom. По данным Ericsson, с момента установки в январе 2002 года за несколько лет случилась только одна незначительная неполадка, на основании чего надёжность системы по расчётам составила 99,9999999 %[155]. Хотя более реальные оценки, учитывающие многие другие факторы, говорят всё-таки о «пяти девятках», успех маршрутизатора связывают с легкодоступными средствами разработки надёжных параллельных вычислений, встроенными в Erlang[155].
Среди другого известного программного обеспечения, выполненного на Erlang, можно выделить распределённую NoSQL базу данных Riak, спроектированную по принципам Amazon DynamoDB[161], Flussonic (ранее известный как Erlyvideo) — видеостриминговый сервер, поддерживающий несколько протоколов[162]. Для стресс-тестирования распределённых систем можно применять (также распределённый) написанный на Erlang инструмент Tsung, который позволяет эмулировать тысячи (при достаточном количестве тестовых серверов — миллионы) одновременных пользователей[163].
Erlang практически идеально подходят для задач искусственного интеллекта (особенно вычислительного интеллекта, нейроэволюции), основанных на нейронных сетях. Подобное применение возможно благодаря имеющимся у Erlang пяти ключевым свойствам «языка программирования нейронных сетей»: изолированные процессы-нейроны (англ.encapsulation), параллелизм (англ.concurrency, одновременность), механизм обнаружения сбоев, независимость от местоположения (англ.location transparency) и горячая замена кода. Примером такого применения является реализация одного из подходов к нейроэволюции — DXNN[164].
Вокруг технологий Erlang образовалось сообщество разработчиков, не отказывающее в поддержке новичкам. Исходный код Erlang доступен через сервис совместной разработки GitHub. Разработчики и пользователи Erlang могут общаться через список рассылки Erlang-questions (вопросы по Erlang) или на IRC-канале #erlang на Freenode. Erlang Factory (www.erlang-factory.com) устраивает по всему миру мероприятия и конференции, среди которых конференция пользователей Erlang (Erlang User Conference). Специальная группа SIGPLAN ACM регулярно проводит Erlang-мастерскую (Erlang Workshop), а конференция OSCON включает секцию по Erlang[165].
↑ 12Joe Armstrong, Bjarne Däcker, Thomas Lindgren, Håkan Millroth, Erlang product team at Ericsson.Open-source Erlang — White Paper(англ.). Ericsson AB (2013). Архивировано 25 октября 2011 года.
Чезарини Ф., Томпсон С. Программирование в Erlang = Erlang Programming. — М.: ДМК Пресс, 2012. — 488 с. — ISBN 978-5-94074-617-1.
Хеберт Ф. Изучай Erlang во имя добра! = Learn You Some Erlang for Great Good! — М.: ДМК Пресс, 2015. — 686 с. — ISBN 978-5-97060-086-3.
Чезарини Ф., Виноски С. Проектирования масштабируемых систем в Erlang/OTP = Designing for Scalability with Erlang/OTP. — ДМК Пресс, 2017. — 486 с. — ISBN 978-5-97060-212-6.
на английском языке
Francesco Cesarini, Simon Thompson. Erlang Programming. — O’Reilly Media, Inc., 2009. — 498 p. — ISBN 978-0-596-51818-9.
Joe Armstrong. Programming Erlang: Software for a Concurrent World. — Pragmatic Bookshelf, 2007. — 536 p. — ISBN 978-1-93435-600-5.
Dan McCreary, Ann Kelly.10.5. Case study: building NoSQL systems with Erlang // Making Sense of NoSQL: A guide for managers and the rest of us. — Manning Publications, 2013. — 312 p. — ISBN 978-1-61729-107-4.
Sher, Gene I. Handbook of Neuroevolution Through Erlang. — Springer, 2013. — 831 p. — ISBN 978-1-4614-4462-6.
Robert Virding, Claes Wikström, Mike Williams.Concurrent Programming in ERLANG / J. Armstrong. — 2nd ed. — Prentice Hall International (UK) Ltd., 1996. — ISBN 0-13-508301-X.
Maurice Castro.Erlang in Real Time. — Department of Computer Science, RMIT, Australia, 2001. — ISBN 0864447434.
Armstrong, Joe (January 2007). “A History of Erlang”. Proceedings of the Third ACM SIGPLAN Conference on History of Programming Languages. HOPL III. San Diego, California: ACM. pp. 6−1—6-26. DOI:10.1145/1238844.1238850. 978-1-59593-766-7. Дата обращения 2013-12-06. Используется устаревший параметр |month= (справка)
Avgerinos, Thanassis and Sagonas, Konstantinos. Cleaning Up Erlang Code is a Dirty Job but Somebody's Gotta Do It (англ.) // Proceedings of the 8th ACM SIGPLAN Workshop on ERLANG. — Edinburgh, Scotland: ACM, 2009. — doi:10.1145/1596600.1596602.
Aronis, Stavros and Papaspyrou, Nikolaos and Roukounaki, Katerina and Sagonas, Konstantinos and Tsiouris, Yiannis and Venetis, Ioannis E. (2012). “A Scalability Benchmark Suite for Erlang/OTP”. Proceedings of the Eleventh ACM SIGPLAN Workshop on Erlang Workshop. Erlang '12. Copenhagen, Denmark: ACM. pp. 33—42. DOI:10.1145/2364489.2364495. ISBN978-1-4503-1575-3, Проверьте параметр |isbn=: invalid character (справка на английском). 978-1-59593-766-7. Дата обращения 2013-12-09.