Erlang ( / ˈ ɜː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». [8] [11] [12] Erlang был разработан с целью улучшения разработки приложений телефонии. [13] Первоначальная версия Erlang была реализована на Prolog и находилась под влиянием языка программирования PLEX, который использовался в более ранних телефонных станциях Ericsson. К 1988 году Erlang доказал, что он подходит для прототипирования телефонных станций, но интерпретатор Prolog был слишком медленным. Одна группа в Ericsson подсчитала, что он должен быть в 40 раз быстрее, чтобы быть пригодным для использования в производстве. В 1992 году началась работа над виртуальной машиной BEAM (VM), которая компилирует Erlang в C, используя смесь изначально скомпилированного кода и потокового кода для достижения баланса между производительностью и дисковым пространством. [14] По словам соавтора Джо Армстронга, язык перешел от лабораторного продукта к реальным приложениям после краха телефонной станции следующего поколения AXE под названием AXE-N в 1995 году. В результате Erlang был выбран для следующей станции асинхронного режима передачи (ATM) AXD . [8]
В феврале 1998 года Ericsson Radio Systems запретила внутреннее использование Erlang для новых продуктов, ссылаясь на предпочтение непатентованных языков. [15] Запрет заставил Армстронга и других строить планы по уходу из Ericsson. [16] В марте 1998 года Ericsson анонсировала коммутатор AXD301, [8] содержащий более миллиона строк Erlang и сообщавший о достижении высокой доступности в девять «9» . [17] В декабре 1998 года реализация Erlang была открыта, и большая часть команды Erlang ушла в отставку, чтобы сформировать новую компанию Bluetail AB. [8] В конечном итоге Ericsson ослабила запрет и снова наняла Армстронга в 2004 году. [16]
В 2006 году в систему выполнения и виртуальную машину была добавлена поддержка симметричной многопроцессорной обработки . [8]
Приложения Erlang построены из очень легких процессов Erlang в системе выполнения Erlang. Процессы Erlang можно рассматривать как «живые» объекты ( объектно-ориентированное программирование ) с инкапсуляцией данных и передачей сообщений , но способные изменять поведение во время выполнения. Система выполнения Erlang обеспечивает строгую изоляцию процессов между процессами Erlang (сюда входят данные и сборка мусора, разделяемые по отдельности каждым процессом Erlang) и прозрачную связь между процессами (см. Прозрачность расположения ) на разных узлах Erlang (на разных хостах).
Джо Армстронг, один из создателей Erlang, обобщил принципы процессов в своей докторской диссертации : [18]
Джо Армстронг заметил в интервью Rackspace в 2013 году: «Если Java — это « написано один раз, запущено где угодно », то Erlang — это «написано один раз, запущено вечно»». [19]
В 2014 году Ericsson сообщила, что Erlang используется в ее узлах поддержки, а также в мобильных сетях GPRS , 3G и LTE по всему миру, а также компаниями Nortel и Deutsche Telekom . [20]
Erlang используется в RabbitMQ . Как выразился Тим Брей , директор по веб-технологиям в Sun Microsystems , в своем докладе на O'Reilly Open Source Convention (OSCON) в июле 2008 года:
Если бы кто-то пришел ко мне и захотел заплатить мне кучу денег за создание крупномасштабной системы обработки сообщений, которая действительно должна была бы работать постоянно и не могла бы позволить себе останавливаться на годы, я бы без колебаний выбрал Erlang для ее создания.
Erlang — это язык программирования, используемый для кодирования WhatsApp . [21]
Это также выбранный язык для Ejabberd — сервера обмена сообщениями XMPP .
Elixir — это язык программирования, который компилируется в байт-код BEAM (через абстрактный формат Erlang). [22]
С момента выпуска с открытым исходным кодом Erlang распространился за пределы телекоммуникаций, утвердившись на других вертикальных рынках, таких как FinTech, игры, здравоохранение, автомобилестроение, интернет вещей и блокчейн. Помимо WhatsApp, есть и другие компании, перечисленные в качестве историй успеха Erlang: Vocalink (компания MasterCard), Goldman Sachs , Nintendo , AdRoll, Grindr , BT Mobile , Samsung , OpenX и SITA . [23] [24]
Факториальный алгоритм , реализованный на Erlang:
- module ( fact ). % Это файл 'fact.erl', модуль и имя файла должны совпадать - export ([ fac / 1 ]). % Это экспортирует функцию 'fac' арности 1 (1 параметр, без типа, без имени) fac ( 0 ) -> 1 ; % Если 0, то вернуть 1, в противном случае (обратите внимание на точку с запятой ;, означающую 'else') fac ( N ) когда N > 0 , is_integer ( N ) -> N * fac ( N - 1 ). % Рекурсивно определить, затем вернуть результат % (обратите внимание на точку ., означающую 'endif' или 'function end') %% Эта функция завершится сбоем, если будет указано что-либо, кроме неотрицательного целого числа. %% Это иллюстрирует философию Erlang «Пусть она рухнет».
Алгоритм хвостовой рекурсии, который создает последовательность Фибоначчи :
%% Объявление модуля должно соответствовать имени файла "series.erl" - module ( series ).%% Оператор экспорта содержит список всех тех функций, которые формируют %% открытый API модуля. В этом случае этот модуль предоставляет одну %% функцию с именем fib, которая принимает 1 аргумент (IE имеет арность 1) %% Общий синтаксис для -export представляет собой список, содержащий имя и %% арность каждой открытой функции - export ([ fib / 1 ]).%% --------------------------------------------------------------------- %% Публичный API %% ---------------------------------------------------------------------%% Обрабатывать случаи, в которых fib/1 получает определенные значения %% Порядок, в котором объявляются эти сигнатуры функций, является важной %% частью функциональности этого модуля%% Если fib/1 получает отрицательное число, вернуть атом err_neg_val %% Обычно такое защитное кодирование не рекомендуется из-за философии Erlang «Пусть %% это рухнет», но здесь результатом будет бесконечный цикл. fib ( N ) когда N < 0 -> err_neg_val ; %% Если fib/1 передано точно целое число 0, то вернуть 0 fib ( 0 ) -> 0 ; %% Для всех остальных значений вызовите закрытую функцию fib_int/3 для выполнения %% вычисления fib ( N ) -> fib_int ( N - 1 , 0 , 1 ). %% --------------------------------------------------------------------- %% Частный API %% ---------------------------------------------------------------------%% Если fib_int/3 получает 0 в качестве первого аргумента, то все готово, поэтому %% возвращает значение аргумента B. Второй аргумент обозначается _ для %% для игнорирования его значения. fib_int ( 0 , _, B ) -> B ; %% Для всех остальных комбинаций аргументов рекурсивно вызвать fib_int/3 %% где каждый вызов делает следующее: %% - уменьшить счетчик N %% - передать третий аргумент как новый второй аргумент %% - передать сумму второго и третьего аргументов как новый %% третий аргумент fib_int ( N , A , B ) -> fib_int ( N - 1 , B , A + B ).
Исключение комментариев делает программу гораздо короче.
- модуль ( серия ). - экспорт ([ fib / 1 ]).fib ( N ) когда N < 0 -> err_neg_val ; fib ( 0 ) -> 0 ; fib ( N ) -> fib_int ( N - 1 , 0 , 1 ). fib_int ( 0 , _, 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' % затем 'Pivot' затем 'Back' для всех элементов, которые должны быть после 'Pivot' qsort ([ Front || Front <- Rest , Front < Pivot ]) ++ [ Pivot ] ++ qsort ([ Back || Back <- Rest , Back >= Pivot ]).
Приведенный выше пример рекурсивно вызывает функцию qsort
до тех пор, пока не останется ничего для сортировки. Выражение [Front || Front <- Rest, Front < Pivot]
представляет собой списочное включение , означающее «Создать список элементов, Front
такой что Front
является членом Rest
и Front
меньше Pivot
». ++
— оператор конкатенации списков.
Для более сложных структур в целях удобства чтения можно использовать функцию сравнения.
Следующий код сортирует списки по длине:
% Это файл 'listsort.erl' (так устроен компилятор) - модуль ( listsort ). % Экспортируем 'by_length' с 1 параметром (тип и имя не важны) - export ([ by_length / 1 ]).by_length ( Lists ) -> % Использовать 'qsort/2' и предоставлять анонимную функцию в качестве параметра qsort ( Lists , fun ( A , B ) -> length ( A ) < length ( B ) end ). qsort ([], _) -> []; % Если список пуст, вернуть пустой список (игнорировать второй параметр) qsort ([ Pivot | Rest ], Smaller ) -> % Разбить список с элементами 'Smaller' перед 'Pivot' и не-'Smaller' элементами % после 'Pivot' и отсортировать подсписки. qsort ([ X || X <- Rest , Smaller ( X , Pivot )], Smaller ) ++ [ Pivot ] ++ qsort ([ Y || Y <- Rest , not ( Smaller ( Y , Pivot ))], Smaller ).
A Pivot
берется из первого параметра, указанного в qsort()
, а остальная часть Lists
называется Rest
. Обратите внимание, что выражение
[ X || 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". ServerProcess ! { pause , 10 }, % Прием сообщений, отправленных этому процессу receive a_message -> do_something ; { data , DataContent } -> handle ( DataContent ); { hello , Text } -> io : format ( "Получил приветственное сообщение: ~s " , [ Text ]); { goodbye , Text } -> io : format ( "Получил прощальное сообщение: ~s " , [ Text ]) end .
Как показывает пример, процессы могут создаваться на удаленных узлах, а связь с ними прозрачна в том смысле, что связь с удаленными процессами работает точно так же, как связь с локальными процессами.
Параллелизм поддерживает основной метод обработки ошибок в Erlang. Когда процесс аварийно завершается, он аккуратно завершает работу и отправляет сообщение контролирующему процессу, который затем может предпринять действия, например, запустить новый процесс, который возьмет на себя задачу старого процесса. [34] [35]
Официальная эталонная реализация Erlang использует BEAM . [36] BEAM включен в официальный дистрибутив Erlang, называемый Erlang/OTP. BEAM выполняет байт-код , который преобразуется в потоковый код во время загрузки. Он также включает в себя компилятор собственного кода на большинстве платформ, разработанный High Performance Erlang Project (HiPE) в Университете Уппсалы . С октября 2001 года система HiPE полностью интегрирована в систему Erlang/OTP с открытым исходным кодом Ericsson. [37] Он также поддерживает интерпретацию, непосредственно из исходного кода через абстрактное синтаксическое дерево , через скрипт, начиная с версии R11B-5 Erlang.
Erlang поддерживает динамическое обновление программного обеспечения на уровне языка . Для реализации этого код загружается и управляется как «модульные» единицы; модуль является единицей компиляции . Система может одновременно хранить в памяти две версии модуля, и процессы могут одновременно запускать код из каждой из них. Версии называются «новой» и «старой» версиями. Процесс не перейдет в новую версию, пока не сделает внешний вызов своего модуля.
Пример механизма горячей загрузки кода:
%% Процесс, единственной задачей которого является ведение счетчика. %% Первая версия - модуль ( counter ). - export ([ start / 0 , codeswitch / 1 ]). начало () -> цикл ( 0 ). loop ( Sum ) -> receive { increment , Count } -> loop ( Sum + Count ); { counter , Pid } -> Pid ! { counter , Sum }, loop ( Sum ); code_switch -> ? MODULE : codeswitch ( Sum ) % Принудительно использовать 'codeswitch/1' из последней версии MODULE end . кодовый переключатель ( Sum ) -> цикл ( Sum ).
Во второй версии мы добавляем возможность обнуления счетчика.
%% Вторая версия - модуль ( счетчик ). - экспорт ([ старт / 0 , кодовыйпереключатель / 1 ]). начало () -> цикл ( 0 ). цикл ( Сумма ) -> получить { инкремент , Счетчик } -> цикл ( Сумма + Счетчик ); сброс -> цикл ( 0 ); { счетчик , Pid } -> Pid ! { счетчик , Сумма }, цикл ( Сумма ); code_switch -> ? МОДУЛЬ : codeswitch ( Сумма ) конец . кодовый переключатель ( Sum ) -> цикл ( Sum ).
Только при получении сообщения, состоящего из атома, code_switch
цикл выполнит внешний вызов codeswitch/1 ( ?MODULE
— макрос препроцессора для текущего модуля). Если в памяти есть новая версия модуля счетчика , то будет вызвана его функция codeswitch/1. Практика наличия определенной точки входа в новую версию позволяет программисту преобразовывать состояние в то, что необходимо в новой версии. В этом примере состояние сохраняется как целое число.
На практике системы строятся с использованием принципов проектирования Open Telecom Platform, что приводит к большему количеству проектов, которые можно обновлять с помощью кода. Успешная горячая загрузка кода требовательна. Код должен быть написан с осторожностью, чтобы использовать возможности Erlang.
В 1998 году Ericsson выпустила Erlang как бесплатное программное обеспечение с открытым исходным кодом , чтобы обеспечить его независимость от одного поставщика и повысить осведомленность о языке. Erlang вместе с библиотеками и распределенной базой данных реального времени Mnesia образует коллекцию библиотек OTP. Ericsson и несколько других компаний поддерживают Erlang в коммерческих целях.
С момента выпуска с открытым исходным кодом Erlang использовался несколькими фирмами по всему миру, включая Nortel и Deutsche Telekom . [38] Хотя Erlang был разработан, чтобы заполнить нишу, и оставался малоизвестным языком большую часть своего существования, его популярность растет из-за спроса на параллельные сервисы. [39] [40] Erlang нашел некоторое применение в создании серверов для многопользовательских ролевых онлайн-игр (MMORPG). [41]
{{cite book}}
: |journal=
проигнорировано ( помощь )Erlang концептуально похож на язык программирования Оккама, хотя он перерабатывает идеи CSP в функциональной структуре и использует асинхронную передачу сообщений.
пользователем Erlang является (сюрприз!) Ericsson. Ericsson использует его для написания программного обеспечения, используемого в телекоммуникационных системах. Многие десятки проектов использовали его, особенно крупным является чрезвычайно масштабируемый коммутатор ATM AXD301.
Другие коммерческие пользователи, перечисленные в разделе FAQ, включают: Nortel, Deutsche Flugsicherung (немецкая национальная организация
по управлению воздушным движением
) и T-Mobile.
Практически все языки используют параллелизм с общим состоянием. Это очень сложно и приводит к ужасным проблемам, когда вы обрабатываете сбои и масштабируете систему... Некоторые довольно быстрорастущие стартапы в финансовом мире зацепились за Erlang; например, шведский www.kreditor.se.
не верю, что другие языки смогут догнать Erlang в ближайшее время. Им будет легко добавлять языковые возможности, чтобы быть похожими на Erlang. Им потребуется много времени, чтобы создать такую высококачественную виртуальную машину и зрелые библиотеки для параллелизма и надежности. Так что Erlang готов к успеху. Если вы хотите создать многоядерное приложение в ближайшие несколько лет, вам следует обратить внимание на Erlang.