Эрланг ( / ˈ ɜːr l æ ŋ / UR -lang ) — это универсальный , параллельный , функциональный язык программирования высокого уровня и система времени выполнения со сборкой мусора . Термин Erlang используется взаимозаменяемо с Erlang/OTP или Open Telecom Platform (OTP), которая состоит из системы времени выполнения Erlang , нескольких готовых к использованию компонентов (OTP), в основном написанных на Erlang, и набора принципов проектирования для Erlang. программы. [5]
Система времени выполнения Erlang предназначена для систем со следующими характеристиками:
Язык программирования Erlang имеет неизменяемые данные, сопоставление с образцом и функциональное программирование . [7] Последовательное подмножество языка Erlang поддерживает быстрое вычисление , однократное присваивание и динамическую типизацию .
Обычное приложение Erlang состоит из сотен небольших процессов Erlang.
Первоначально это было проприетарное программное обеспечение компании Ericsson , разработанное Джо Армстронгом , Робертом Вирдингом и Майком Уильямсом в 1986 году, [8] но было выпущено как бесплатное программное обеспечение с открытым исходным кодом в 1998 году. [9] [10] Erlang/OTP поддерживается и поддерживается подразделением продуктов Open Telecom Platform (OTP) компании Ericsson .
Те, кто работал над телефонными коммутаторами (для которых был разработан этот язык), предположили, что имя Erlang , приписываемое Бьярну Дэккеру, является отсылкой к датскому математику и инженеру Агнеру Крарупу Эрлангу и слоговой аббревиатурой «Ericsson Language». [8] [11] [12] Erlang был разработан с целью улучшения разработки телефонных приложений. [13] Первоначальная версия Erlang была реализована на Прологе и находилась под влиянием языка программирования PLEX, использовавшегося в более ранних биржах Ericsson. К 1988 году Эрланг доказал, что он пригоден для создания прототипов телефонных станций, но интерпретатор Пролога был слишком медленным. Одна группа из Ericsson подсчитала, что для промышленного использования он должен быть в 40 раз быстрее. В 1992 году началась работа над виртуальной машиной BEAM (VM), которая компилирует Erlang в C, используя сочетание скомпилированного кода и многопоточного кода для достижения баланса между производительностью и дисковым пространством. [14] По словам соавтора Джо Армстронга, язык перешел от лабораторного продукта к реальным приложениям после краха телефонной станции AX следующего поколения под названием AXE-N в 1995 году. В результате Erlang был выбран для следующей асинхронной передачи. Режим (АТМ) обмена AXD . [8]
В феврале 1998 года Ericsson Radio Systems запретила внутреннее использование Erlang для новых продуктов, сославшись на предпочтение непатентованных языков. [15] Запрет заставил Армстронга и других строить планы покинуть Ericsson. [16] В марте 1998 года Эрикссон анонсировал коммутатор AXD301, [8] содержащий более миллиона строк Erlang и сообщивший о достижении высокой доступности девяти «9» . [17] В декабре 1998 года реализация Erlang была открыта, и большая часть команды Erlang ушла в отставку, чтобы сформировать новую компанию Bluetail AB. [8] В конце концов Эрикссон ослабил запрет и повторно нанял Армстронга в 2004 году. [16]
В 2006 году в систему выполнения и виртуальную машину была добавлена встроенная поддержка симметричной многопроцессорности . [8]
Приложения Erlang состоят из очень легких процессов Erlang в системе времени выполнения Erlang. Процессы Erlang можно рассматривать как «живые» объекты ( объектно-ориентированное программирование ), с инкапсуляцией данных и передачей сообщений , но способные изменять поведение во время выполнения. Система времени выполнения Erlang обеспечивает строгую изоляцию процессов между процессами Erlang (сюда входят данные и сбор мусора, разделенные индивидуально каждым процессом Erlang) и прозрачную связь между процессами (см. Прозрачность расположения ) на разных узлах Erlang (на разных хостах).
Джо Армстронг, соавтор Эрланга, обобщил принципы процессов в своей докторской диссертации : [18]
Джо Армстронг заметил в интервью Rackspace в 2013 году: «Если Java — это « напиши один раз, запускай где угодно », то Erlang — это «напиши один раз, работай вечно». [19]
В 2014 году Ericsson сообщила, что Erlang используется в ее узлах поддержки, а также в мобильных сетях GPRS , 3G и LTE по всему миру, а также Nortel и T-Mobile . [20]
Эрланг используется в RabbitMQ . Как сказал Тим Брэй , директор веб-технологий Sun Microsystems , в своем программном докладе на конференции O'Reilly Open Source Convention (OSCON) в июле 2008 года:
Если бы кто-то пришел ко мне и захотел заплатить мне много денег за создание крупномасштабной системы обработки сообщений, которая действительно должна была бы постоянно работать и не могла позволить себе выходить из строя в течение многих лет, я бы без колебаний выбрал Erlang встроить его.
Erlang — это язык программирования, используемый для написания кода WhatsApp . [21]
Это также язык, выбранный для Ejabberd — сервера обмена сообщениями XMPP .
Эликсир — это язык программирования, который компилируется в байт-код BEAM (через абстрактный формат Erlang). [22]
С момента выпуска с открытым исходным кодом Erlang вышел за пределы телекоммуникаций, зарекомендовав себя на других вертикальных рынках, таких как FinTech, игры, здравоохранение, автомобилестроение, Интернет вещей и блокчейн. Помимо WhatsApp, в список историй успеха Erlang входят и другие компании: Vocalink (компания MasterCard), Goldman Sachs , Nintendo , AdRoll, Grindr , BT Mobile , Samsung , OpenX и SITA . [23] [24]
Алгоритм факториала , реализованный в Erlang:
- модуль ( факт ). % Это файл «fact.erl», модуль и имя файла должны совпадать — экспорт ([ fac / 1 ]). % Экспортирует функцию 'fac' с арностью 1 (1 параметр, без типа, без имени) факт ( 0 ) -> 1 ; % Если 0, то вернуть 1, в противном случае (обратите внимание на точку с запятой; означает «иначе») fac ( N ), когда N > 0 , is_integer ( N ) -> N * fac ( N - 1 ). % Рекурсивно определить, а затем вернуть результат % (обратите внимание на точку ., означающую 'endif' или 'конец функции') %% Эта функция завершится сбоем, если будет задано что-либо, кроме неотрицательного целого числа. %% Он иллюстрирует философию Эрланга «Пусть рухнет».
Алгоритм хвостовой рекурсии, создающий последовательность Фибоначчи :
%% Объявление модуля должно совпадать с именем файла «series.erl» — модуль ( series ).%% Оператор экспорта содержит список всех тех функций, которые формируют %% общедоступный API модуля. В этом случае этот модуль предоставляет одну функцию %%, называемую fib, которая принимает 1 аргумент (IE имеет арность 1) %% Общий синтаксис для -export представляет собой список, содержащий имя и %% арность каждой общедоступной функции - экспорт ([ фиб / 1 ]).%% -------------------------------------------------- --------------------- %% Публичный API %% ----------------------- ----------------------------------------------%% Обработка случаев, когда fib/1 получает определенные значения %% Порядок объявления этих сигнатур функций является жизненно важной %% частью функциональности этого модуля.%% Если fib/1 передано именно целое число 0, то вернуть 0 fib ( 0 ) -> 0 ; %% Если fib/1 получает отрицательное число, то вернуть атом err_neg_val %% Обычно такое защитное кодирование не рекомендуется из-за философии Эрланга «Пусть %% произойдет сбой»; однако в этом случае мы должны явно %% предотвратить ситуацию, которая приведет к сбою механизма выполнения Erlang fib ( N ) , когда N < 0 -> err_neg_val ; %% Если fib/1 передается целое число меньше 3, то возвращается 1 %% Предыдущие две сигнатуры функции обрабатывают все случаи, когда N < 1, %%, поэтому эта сигнатура функции обрабатывает случаи, когда N = 1 или N = 2 fib ( N ) когда N < 3 -> 1 ; %% Для всех остальных значений вызовите частную функцию fib_int/3, чтобы выполнить %% вычисление fib ( N ) -> fib_int ( N , 0 , 1 ). %% -------------------------------------------------- --------------------- %% Частный API %% ----------------------- ----------------------------------------------%% Если fib_int/3 получает 1 в качестве первого аргумента, то все готово, поэтому %% возвращает значение аргумента B. Поскольку нас не интересует значение %% второго аргумента, мы обозначаем это с помощью _ чтобы указать значение %% «не важно» fib_int ( 1 , _, B ) -> B ; %% Для всех остальных комбинаций аргументов рекурсивно вызывайте fib_int/3 %%, где каждый вызов выполняет следующее: %% — уменьшает счетчик N %% — берет предыдущее значение Фибоначчи в аргументе B и передает его как %% аргумент A %% — Вычислите значение текущего числа Фибоначчи и передайте его %% в качестве аргумента B fib_int ( N , A , B ) -> fib_int ( N - 1 , B , A + B ).
Вот та же программа без поясняющих комментариев:
- модуль ( серия ). - экспорт ([ фиб / 1 ]).фиб ( 0 ) -> 0 ; fib ( N ) , когда N < 0 -> err_neg_val ; fib ( N ) когда N < 3 -> 1 ; фиб ( N ) -> фиб_int ( N , 0 , 1 ). fib_int ( 1 , _, B ) -> B ; fib_int ( N , A , B ) -> fib_int ( N - 1 , B , A + B ).
Быстрая сортировка в Erlang с использованием понимания списка : [25]
%% qsort:qsort(List) %% Сортировка списка элементов - модуль ( qsort ). % Это файл 'qsort.erl' — экспорт ([ qsort / 1 ]). % Экспортируется функция qsort с 1 параметром (без типа и имени) qsort ([]) -> []; % Если список [] пуст, вернуть пустой список (нечего сортировать) qsort ([ Pivot | Rest ]) -> % Рекурсивно составить список с 'Front' для всех элементов, которые должны быть до 'Pivot' % then ' Pivot', затем 'Back' для всех элементов, которые должны быть после 'Pivot' qsort ([ Front || Front <- Rest , Front < Pivot ]) ++ [ Pivot ] ++ qsort ([ Back || Back <- Rest , Назад >= Разворот ]).
В приведенном выше примере функция рекурсивно вызывается qsort
до тех пор, пока ничего не останется для сортировки. Выражение [Front || Front <- Rest, Front < Pivot]
представляет собой понимание списка , что означает «Создать список элементов Front
, который Front
является членом Rest
и Front
меньше Pivot
». ++
— оператор конкатенации списков.
Функцию сравнения можно использовать для более сложных структур ради удобства чтения.
Следующий код будет сортировать списки по длине:
% Это файл 'listsort.erl' (так устроен компилятор) - модуль ( listsort ). % Экспортировать «по_длине» с 1 параметром (тип и имя не важны) — экспорт ([ по_длине / 1 ]).by_length ( Lists ) -> % Используйте 'qsort/2' и предоставляет анонимную функцию в качестве параметра qsort ( Lists , fun ( A , B ) -> length ( A ) < length ( B ) end ). qsort ([], _) -> []; % Если список пуст, вернуть пустой список (игнорировать второй параметр) qsort ([ Pivot | Rest ], Smaller ) -> % Список разделов с элементами «Меньший» перед элементами «Опорный» и не- «Меньший» % после «Pivot» и отсортируйте подсписки. qsort ([ X || X <- Rest , Smaller ( X , Pivot )], Smaller ) ++ [ Pivot ] ++ qsort ([ Y || Y <- Rest , not ( Smaller ( Y , Pivot ))], Меньше ).
A Pivot
берется из первого параметра, заданного для, qsort()
а остальная часть Lists
называется Rest
. Обратите внимание, что выражение
[ Икс || X <- Отдых , Меньше ( X , Поворот )]
по форме ничем не отличается от
[ Спереди || Передняя <- Отдых , Передняя < Поворот ]
(в предыдущем примере), за исключением использования функции сравнения в последней части, говорящей: «Постройте список элементов, X
таких, которые X
являются членами Rest
и Smaller
являются истинными», с Smaller
определенным ранее как
удовольствие ( A , B ) -> длина ( A ) < длина ( B ) конец
Анонимная функция указывается Smaller
в списке параметров второго определения, qsort
чтобы на нее можно было ссылаться по этому имени внутри этой функции. Она не названа в первом определении функции qsort
, которое имеет дело с базовым случаем пустого списка и поэтому не нуждается в этой функции, не говоря уже о ее имени.
В Erlang имеется восемь примитивных типов данных :
make_ref()
.spawn(...)
Pids являются ссылками на процессы Erlang.open_port
. Сообщения можно отправлять и получать из портов, но эти сообщения должны подчиняться так называемому «протоколу порта».fun(...) -> ... end
.И три составных типа данных:
{D1,D2,...,Dn}
обозначает кортеж, аргументы которого: D1, D2, ... Dn.
Аргументы могут быть примитивными типами данных или составными типами данных. Доступ к любому элементу кортежа возможен за постоянное время.[Dh|Dt]
обозначает список, первым элементом которого является Dh
, а остальные элементы — список Dt
. Синтаксис []
обозначает пустой список. Синтаксис [D1,D2,..,Dn]
является сокращением от [D1|[D2|..|[Dn|[]]]]
. Доступ к первому элементу списка возможен за постоянное время. Первый элемент списка называется главой списка . Оставшаяся часть списка после удаления его заголовка называется хвостом списка .#{Key1=>Value1,...,KeyN=>ValueN}
.Предусмотрены две формы синтаксического сахара :
[99,97,116]
. [26]В Erlang нет метода определения классов, хотя доступны внешние библиотеки . [27]
Erlang разработан с использованием механизма, который позволяет внешним процессам легко отслеживать сбои (или аппаратные сбои), а не внутрипроцессный механизм, такой как обработка исключений, используемый во многих других языках программирования. О сбоях сообщается так же, как и о других сообщениях, и это единственный способ взаимодействия процессов друг с другом [28] , а подпроцессы можно создавать дешево (см. ниже). Философия «позволить сбою» предпочитает полностью перезапустить процесс, а не пытаться восстановиться после серьезного сбоя. [29] Хотя эта философия по-прежнему требует обработки ошибок, эта философия приводит к тому, что для защитного программирования выделяется меньше кода , где код обработки ошибок очень контекстуален и специфичен. [28]
Типичное приложение Erlang написано в виде дерева супервизоров. Эта архитектура основана на иерархии процессов, в которой процесс верхнего уровня известен как «супервизор». Затем супервизор порождает несколько дочерних процессов, которые действуют либо как рабочие, либо как супервизоры более низкого уровня. Такие иерархии могут существовать на произвольной глубине и доказали, что они обеспечивают высокомасштабируемую и отказоустойчивую среду, в которой могут быть реализованы функциональные возможности приложения.
В дереве супервизоров все процессы-супервизоры отвечают за управление жизненным циклом своих дочерних процессов, включая обработку ситуаций, в которых эти дочерние процессы выходят из строя. Любой процесс может стать супервизором, сначала создав дочерний процесс, а затем вызвав erlang:monitor/2
его. Если затем отслеживаемый процесс выйдет из строя, супервизор получит сообщение, содержащее кортеж, первым членом которого является атом 'DOWN'
. Супервизор несет ответственность, во-первых, за прослушивание таких сообщений и, во-вторых, за принятие соответствующих мер по исправлению ошибочного состояния.
Основная сила Erlang — поддержка параллелизма . Он имеет небольшой, но мощный набор примитивов для создания процессов и взаимодействия между ними. Erlang концептуально похож на язык occam , хотя он переосмысливает идеи взаимодействия последовательных процессов (CSP) в функциональной структуре и использует асинхронную передачу сообщений. [30] Процессы являются основным средством структурирования приложения Erlang. Это не процессы и не потоки операционной системы , а легкие процессы , запланированные BEAM. Подобно процессам операционной системы (но в отличие от потоков операционной системы), они не имеют общего состояния друг с другом. Предполагаемый минимальный объем накладных расходов для каждого составляет 300 слов . [31] Таким образом, многие процессы могут быть созданы без снижения производительности. В 2005 году тест с 20 миллионами процессов был успешно выполнен с использованием 64-битного Erlang на машине с оперативной памятью 16 ГБ (ОЗУ; всего 800 байт на процесс). [32] Erlang поддерживает симметричную многопроцессорную обработку начиная с версии R11B от мая 2006 года.
Хотя потоки нуждаются в поддержке внешних библиотек на большинстве языков, Erlang предоставляет функции уровня языка для создания процессов и управления ими с целью упрощения параллельного программирования. Хотя в Erlang весь параллелизм является явным, процессы взаимодействуют, используя передачу сообщений вместо общих переменных, что устраняет необходимость в явных блокировках (схема блокировки по-прежнему используется внутри виртуальной машины). [33]
Межпроцессное взаимодействие работает через асинхронную систему передачи сообщений без общего доступа : каждый процесс имеет «почтовый ящик» — очередь сообщений, которые были отправлены другими процессами и еще не использованы. Процесс использует примитив для получения сообщений, соответствующих желаемым шаблонам. Процедура обработки сообщений по очереди проверяет сообщения на соответствие каждому шаблону, пока один из них не совпадет. Когда сообщение потребляется и удаляется из почтового ящика, процесс возобновляет выполнение. Сообщение может содержать любую структуру Erlang, включая примитивы (целые числа, числа с плавающей запятой, символы, атомы), кортежи, списки и функции.receive
В примере кода ниже показана встроенная поддержка распределенных процессов:
% Создайте процесс и вызовите функцию web:start_server(Port, MaxConnections) ServerProcess = spawn ( web , start_server , [ Port , MaxConnections ]), % Создайте удаленный процесс и вызовите функцию % web:start_server(Port, MaxConnections) на компьютере RemoteNode RemoteProcess = spawn ( RemoteNode , web , start_server , [ Port , MaxConnections ]), % Отправить сообщение в ServerProcess (асинхронно). Сообщение состоит из кортежа % с атомом «пауза» и цифрой «10». Серверный процесс ! { пауза , 10 }, % Получать сообщения, отправленные этому процессу, get a_message -> do_something ; { данные , DataContent } -> дескриптор ( DataContent ); { hello , Text } -> io : format ( «Получил приветственное сообщение: ~s » , [ Text ]); { до свидания , текст } -> io : format ( «Получил прощальное сообщение: ~s » , [ текст ]) end .
Как показывает пример, процессы могут создаваться на удаленных узлах, и связь с ними прозрачна в том смысле, что связь с удаленными процессами работает точно так же, как связь с локальными процессами.
Параллелизм поддерживает основной метод обработки ошибок в Erlang. Когда процесс выходит из строя, он аккуратно завершает работу и отправляет сообщение управляющему процессу, который затем может предпринять действия, например запустить новый процесс, который возьмет на себя задачу старого процесса. [34] [35]
Официальная эталонная реализация Erlang использует BEAM . [36] BEAM включен в официальный дистрибутив Erlang под названием Erlang/OTP. BEAM выполняет байт-код , который преобразуется в многопоточный код во время загрузки. Он также включает в себя компилятор собственного кода для большинства платформ, разработанный проектом High Performance Erlang Project (HiPE) в Университете Упсалы . С октября 2001 года система HiPE полностью интегрирована в систему Erlang/OTP с открытым исходным кодом Ericsson. [37] Он также поддерживает интерпретацию непосредственно из исходного кода через абстрактное синтаксическое дерево и через скрипт, начиная с версии Erlang R11B-5.
Erlang поддерживает динамическое обновление программного обеспечения на уровне языка . Для реализации этого код загружается и управляется как «модуль»; модуль является единицей компиляции . Система может одновременно хранить в памяти две версии модуля, и процессы могут одновременно запускать код каждой из них. Версии называются «новой» и «старой» версией. Процесс не перейдет в новую версию, пока не выполнит внешний вызов своего модуля.
Пример механизма загрузки горячего кода:
%% Процесс, единственной задачей которого является ведение счетчика. %% Первая версия — модуль ( счетчик ). - экспорт ([ start / 0 , codeswitch / 1 ]). старт () -> цикл ( 0 ). цикл ( Сумма ) -> получение { приращение , Счетчик } -> цикл ( Сумма + Счет ); { счетчик , Pid } -> Pid ! { счетчик , сумма }, цикл ( сумма ); код_переключатель -> ? MODULE : codeswitch ( Sum ) % Принудительно использовать 'codeswitch/1' из последней версии MODULE end . кодовый переключатель ( Sum ) -> цикл ( Sum ).
Для второй версии мы добавляем возможность обнулить счетчик.
%% Вторая версия — модуль ( счетчик ). - экспорт ([ start / 0 , codeswitch / 1 ]). старт () -> цикл ( 0 ). цикл ( Сумма ) -> получение { приращение , Счетчик } -> цикл ( Сумма + Счет ); сброс -> цикл ( 0 ); { счетчик , Pid } -> Pid ! { счетчик , сумма }, цикл ( сумма ); код_переключатель -> ? МОДУЛЬ : кодовый переключатель ( Сумма ) конец . кодовый переключатель ( Sum ) -> цикл ( Sum ).
Только при получении сообщения, состоящего из атома, code_switch
цикл выполнит внешний вызов codewitch/1 ( ?MODULE
это макрос препроцессора для текущего модуля). Если в памяти имеется новая версия модуля счетчика , то будет вызвана его функция codeswitch/1. Практика использования конкретной точки входа в новую версию позволяет программисту преобразовывать состояние в то, что необходимо в более новой версии. В примере состояние сохраняется как целое число.
На практике системы создаются с использованием принципов проектирования Open Telecom Platform, что приводит к увеличению количества проектов, допускающих обновление кода. Успешная загрузка горячего кода требует усилий. Код должен быть написан с осторожностью, чтобы использовать возможности Erlang.
В 1998 году Ericsson выпустила Erlang как бесплатное программное обеспечение с открытым исходным кодом , чтобы обеспечить его независимость от одного поставщика и повысить осведомленность о языке. Erlang вместе с библиотеками и распределенной базой данных реального времени Mnesia образует коллекцию библиотек OTP. Ericsson и несколько других компаний поддерживают Erlang на коммерческой основе.
С момента выпуска с открытым исходным кодом Erlang использовался несколькими фирмами по всему миру, включая Nortel и T-Mobile . [38] Хотя Erlang был разработан для заполнения ниши и оставался малоизвестным языком на протяжении большей части своего существования, его популярность растет из-за спроса на параллельные сервисы. [39] [40] Эрланг нашел некоторое применение в создании серверов для многопользовательских ролевых онлайн-игр (MMORPG). [41]
{{cite book}}
: |journal=
игнорируется ( помощь )Erlang концептуально похож на язык программирования occam, хотя он переосмысливает идеи CSP в функциональной структуре и использует асинхронную передачу сообщений.
Крупнейшим пользователем Erlang является (сюрприз!) Ericsson.
Ericsson использует его для написания программного обеспечения, используемого в телекоммуникационных системах.
Его использовали многие десятки проектов, особенно крупным из них является чрезвычайно масштабируемый ATM-коммутатор AXD301.
Другие коммерческие пользователи, перечисленные в разделе часто задаваемых вопросов, включают: Nortel, Deutsche Flugsicherung (немецкая национальная организация
управления воздушным движением
) и T-Mobile.
Практически все языки используют параллелизм с общим состоянием.
Это очень сложно и приводит к ужасным проблемам, когда вы справляетесь с сбоями и масштабируете систему... Некоторые довольно быстро развивающиеся стартапы в финансовом мире зацепились за Erlang;
например, шведский www.kreditor.se.
Я не верю, что другие языки смогут догнать Erlang в ближайшее время.
Им будет легко добавлять языковые функции, похожие на Erlang.
Им потребуется много времени, чтобы создать такую качественную виртуальную машину и зрелые библиотеки для одновременного выполнения и надежности.
Итак, Erlang готов к успеху.
Если вы хотите создать многоядерное приложение в ближайшие несколько лет, вам следует обратить внимание на Erlang.