unix 2014-07-21 15-09-16
Erlang [ˈɜːlæŋ] - функциональный язык программирования со строгой динамической типизацией, предназначенный для создания распределённых вычислительных систем. Разработан и поддерживается компанией Ericsson. Язык включает в себя средства порождения параллельных облегчённых процессов и их взаимодействия через обмен асинхронными сообщениями в соответствии с моделью акторов.
Erlang был целенаправленно разработан для применения в распределённых, отказоустойчивых, параллельных системах реального времени, для которых кроме средств самого языка имеется стандартная библиотека модулей и библиотека шаблонных решений (так называемых поведений) - фреймворк OTP (англ. Open Telecom Platform). Программа на Erlang транслируется в байт-код, исполняемый виртуальными машинами, находящимися на различных узлах распределённой вычислительной сети. Erlang-системы поддерживают горячую замену кода, что позволяет эксплуатировать оборудование безостановочно.
Свой синтаксис и некоторые концепции Erlang унаследовал от языка логического программирования Пролог. Язык поддерживает многие типы данных, условные конструкции, сопоставление с образцом, обработку исключений, списковые включения и выражения битовых строк, функции (анонимные функции, функции высшего порядка, рекурсивные определения функций, оптимизацию хвостовой рекурсии), модули, приём и отправку сообщений между процессами. Препроцессор поддерживает работу с макросами и включение заголовочных файлов.
Популярность Erlang начала расти в связи с расширением его области применения (телекоммуникационные системы) на высоконагруженые параллельные распределённые системы, обслуживающие миллионы пользователей WWW, такие как чаты, системы управления содержимым, веб-серверы и распределённые, требующие масштабирования базы данных. Erlang применяется в нескольких NoSQL-базах данных высокой доступности.
История
В середине 1980-х в компьютерной лаборатории компании Ericsson исследовали применимость существующих на тот момент языков программирования для программного обеспечения телекоммуникационных систем. Джо Армстронг (Joe Armstrong), Роберт Вирдинг (Robert Virding) и Майк Вильямс (Mike Williams) под руководством Бьярне Деккера (Bjarne Däcker), написав прототипы программ на различных языках, пришли к выводу, что ни один из этих языков не имел полного набора возможностей, необходимых в области телекоммуникационных систем. В результате был создан новый язык программирования - Erlang. Своё название язык, вероятно, получил в честь датского математика и инженера Агнера Эрланга, основателя научного направления по изучению сетевого трафика в телекоммуникационных системах. По другой версии, название языка изначально было сокращением от «ericsson language».
Влияние на Erlang оказали ML, Миранда, Ада, Модула-2, CHILL, Пролог. Кроме того, на способ обновления программного обеспечения повлиял Smalltalk и использованные Ericsson проприетарные языки EriPascal и PLEX.
Потребовалось четыре года развития языка и прототипирования с использованием виртуальной машины Пролога, после чего в 1991 году Майк Вильямс переписал виртуальную машину для Erlang на Си. В 1992 году Erlang был впервые использован в коммерческом проекте. В 1995 году вышел новый релиз Erlang, вобравший накопившийся к тому моменту опыт использования языка. Язык был сочтён достаточно развитым для использования в других продуктах компании (решения для широкополосной связи, GPRS, ATM).
В декабре 1995 года случилось событие, которое Джо Армстронг считает решающим для Erlang: проект AXE-N в Ellemtel по созданию нового маршрутизатора (как оборудования, так и системного программного обеспечения на C++) потерпел неудачу. В результате реорганизации проекта удалось, использовав разработанное оборудование и язык программирования Erlang, начать работы над ATM-маршрутизаторами серии AXD. Ресурсов лаборатории для такого проекта оказалось недостаточно, поэтому для работ по Erlang было создано производственное подразделение под названием OTP (Open Telecom Platform). В 1996 году увидел свет одноимённый фреймворк OTP.
Неожиданно, в 1998 году топ-менеджмент Ericsson решил не брать на себя обязательства по разработке и поддержке собственного языка программирования, сосредоточившись вместо этого на Java. Использование Erlang было запрещено в новых проектах Ericsson Radio AB в связи с реализацией плана по аутсорсингу программной технологии компании Rational Inc. Это решение очень сильно повлияло на будущее Erlang: оно привело к открытию кода Erlang под открытой лицензией EPL (аналог Mozilla Public License), а также послужило главной причиной начала распространения языка за пределами создавшей его компании. Основным возражением против открытия исходного кода являлось решение вопросов, касающихся патентов, но эти трудности были преодолены. Вскоре многие из основных разработчиков покинули Ericsson, чтобы организовать собственный бизнес - Bluetail AB.
В начале 2000-х учёные круги стали проявлять интерес к Erlang. С 2002 года стал проводиться ежегодный Erlang Workshop. Ericsson продолжал спонсирование проекта HiPE (от англ. High-Performance Erlang - высокопроизводительный Erlang) уппсальского университета. Проект HiPE занимался эффективной реализацией языка и инструментами для проверки типов, а с 2001 года созданный в группе проекта компилятор в машинный код входит в поставку свободно-распространяемой версии Erlang/OTP. Работы, связанные с Erlang, ведут и другие высшие учебные заведения. Инструменты для рефакторинга созданы в Кентском университете в Великобритании и университете Лоранда Этвёша в Венгрии, инструменты для различных видов тестирования - в Мадридском политехническом университете, техническом университете Чалмерса и Гётеборгском университете.
Когда системы с симметричной многопроцессорностью только начинали завоёвывать рынок серверов и настольных компьютеров, бросая вызов разработчикам программного обеспечения, уже в 2006 году первая версия Erlang с поддержкой SMP была выпущена совместными усилиями команды OTP из Ericsson и команды HiPE. Вскоре после этого вышла первая почти за десятилетие крупная монография по Erlang: «Programming Erlang» Джо Армстронга, после чего многие разработчики «открыли» для себя Erlang/OTP, и язык стал набирать популярность.
Процесс развития языка включает в себя рассмотрение предложений по развитию - EEP (англ. Erlang Enhancement Proposal). Через эти предложения Erlang-сообщество вносит изменения в стандартную поставку Erlang. С внесёнными предложениями можно ознакомиться на веб-странице erlang.org/eeps.
Философия
По свидетельству Майка Вильямса, Erlang задумывался для решения трёх проблем разработки распределённых систем мягкого реального времени с высокой степенью параллелизма: возможности быстрой и эффективной разработки ПО; получения системы, устойчивой к программным и аппаратным сбоям и возможности обновления системы «на лету», без простоя оборудования.
По словам Вильямса, философия, которой придерживались разработчики Erlang, подходит и для разработки программного обеспечения на этом языке:
Найдите наиболее подходящие методы. Проектируйте с использованием прототипов;
Одних идей мало: нужно уметь их реализовать и знать, как они работают;
Делайте ошибки в небольшом масштабе, а не в производственном проекте.
Оригинальный текст
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 был разработан специально на основе требований к распределённым, отказоустойчивым, параллельным системам реального времени. С развитием сети Интернет оказалось, что многие приложения имеют аналогичные требования, чем и объясняется растущий интерес к языку.
Высокая отказоустойчивость кроется в применении изолированных друг от друга облегчённых процессов, связанных лишь механизмом обмена сообщениями и сигналами выхода. Принцип разработчиков на Erlang по отношению к обработке ошибочных ситуаций в процессах можно выразить в виде высказывания:
Позвольте приложению упасть, и пускай кто-то другой займётся им
Оригинальный текст
Let it crash and let someone else deal with it
или сокращённо - «let it crash» («пусть падает»). Связано это с тем, что в Erlang-системе легко следить за завершением процесса, завершать процессы, связанные со сбойным, и запускать новые процессы.
Основные особенности
Высокоуровневые конструкции
Erlang является декларативным языком программирования, который скорее используется для описания того, что должно быть вычислено нежели как. Например, определение функции, которое использует сопоставление с образцом для выбора одного из вариантов вычисления или извлечения элемента данных из составной структуры, напоминает уравнение. Сопоставление с образцом распространено даже на битовые строки, что упрощает реализацию телекоммуникационных протоколов.
Функции являются объектами первого класса в Erlang. В языке также широко применяются характерные для функциональной парадигмы программирования списковые включения (генераторы списков).
Параллельные вычисления
Erlang
Обмен сообщениями между процессами в Erlang
Отличительной особенностью языка является применение облегчённых процессов в соответствии с моделью акторов. Такой подход позволяет выполнять одновременно сотни тысяч и даже миллионы таких процессов, каждый из которых может иметь скромные требования по памяти. Процессы изолированы друг от друга и не имеют общего состояния, но между ними можно установить связь и получать сообщения об их состоянии. Для взаимодействия процессов используется асинхронный обмен сообщениями. Каждый процесс имеет свою очередь сообщений, обработка которой использует сопоставление с образцом. Процесс, отправивший сообщение, не получает уведомления о доставке, даже если идентификатор процесса-получателя недействителен или получатель игнорирует сообщение. Таким образом, ответственность за правильно организованное взаимодействие между процессами лежит на разработчике.
Например, при реализация на Erlang сетевого чата структура программы может напрямую отражать одновременность действий пользователей по обмену сообщениями путём запуска новых процессов. Эффективность передачи сообщений сохраняется и при увеличении числа процессов, а требования к памяти минимизируются за счёт того, что облегчёнными процессами управляет виртуальная машина, а не средства нижележащей операционной системы.
Распределённые вычисления
Erlang с самого начала проектировался для распределённых вычислений и масштабируемости. Распределение вычислений встроено в синтаксис и семантику языка, поэтому построение системы можно вести, абстрагируясь от конкретного места вычислений. В стандартной поставке Erlang может наладить связь процессов по протоколу TCP/IP независимо от поддерживаемых им нижележащих платформ (операционных систем).
Работающий экземпляр среды выполнения Erlang (англ. Erlang runtime system) называется узлом (англ. node). Программы, написанные на Erlang, способны работать на нескольких узлах. Узлами могут быть процессоры, многие ядра одного процессора, и даже целый кластер машин. Узел имеет имя и «знает» о существовании других узлов на данной машине или в сети. Создание и взаимодействие процессов разных узлов не отличается от организации взаимодействия процессов внутри узла. Для создания процесса на другом узле процессу достаточно знать его имя и, без особых на то оснований, он может не интересоваться физическим расположением взаимодействующего с ним процесса. Синтаксис отправки сообщения процессу на своём узле и удалённом один и тот же.
Благодаря встроенным в язык возможностям распределённых вычислений объединение в кластер, балансировка нагрузки, добавление узлов и серверов, повышение надёжности вызывают лишь небольшие затраты на дополнительный код. По умолчанию узлы спроектированы для работы внутри обособленного сегмента сети (DMZ), но, если необходимо, коммуникация между узлами может происходить с применением защищённого криптографическими методами протокола SSL.
Мягкое реальное время
Программы на высокоуровневом языке Erlang могут быть использованы в системах мягкого реального времени (которое иногда переводят как «псевдореальное» или «квазиреальное»). Автоматизированное управление памятью и сборка мусора действуют в рамках одного процесса, что даёт возможность создавать системы с миллисекундным временем отклика (даже несмотря на необходимость сборки мусора), не испытывающие ухудшения пропускной способности при высокой нагрузке.
Горячая замена кода
Для систем, которые не могут быть остановлены для обновления кода, Erlang предлагает горячую замену кода (англ. hot code upgrade). При этом в приложении могут одновременно работать старая и новая версии кода. Таким способом программное обеспечение на Erlang может быть модернизировано без простоев, а выявленные ошибки исправлены.
Описание языка
Типы данных
Типизация в Erlang является строгой и динамической. Динамическая типизация была выбрана для языка Erlang по причине того, что первые разработчики были больше с ней знакомы. По мнению Джо Армстронга, статическая типизация потребовала бы очень больших трудозатрат, в частности, реализовать систему горячей дозагрузки кода было бы крайне затруднительно. Такая типизация, при которой возможные ошибки типов выявляются только во время выполнения, тем не менее, не помешала создавать системы с очень высоким уровнем доступности. Данные в Erlang являются неизменяемыми: операции не переписывают старые значения, находящиеся в памяти. Если необходимо, модули на Erlang можно снабдить описаниями и определениями новых типов (не влияющими на компиляцию программы) для автоматической проверки типов с помощью утилиты Dialyzer.
Числа
В Erlang есть два типа числовых литералов: целые и с плавающей запятой, например: 4.5e-20, 125. Кроме обычной нотации, числа можно задавать через символ ASCII (например, $B означает 66) или вместе с указанием системы счисления с основанием от 2 до 36 (в старых версиях - до 16), например: 16#3f, 2#1010. В Erlang применяются целые числа произвольной точности и действительные числа двойной точности (64 бита), в стандарте IEEE 754-1985.
Для работы с числами можно использовать модуль math, который содержит обычный набор математических функций и функцию math:pi/0, возвращающую число \pi. Пример вычислений в интерактивной оболочке:
Erlang R15B01 (erts-5.9.1) [source] [64-bit] [smp:4:4] [async-threads:0] [kernel-poll:false]
Eshell V5.9.1 (abort with ^G)
1> 123/23 + 12*(2+3).
65.34782608695652
2> math:cos(math:pi()).
-1.0
3> random:uniform(10).
5
Атомы
Атом - константа с именем, которая должна быть заключена в одинарные кавычки, если не начинается со строчной буквы или содержит знаки, кроме букв, цифр, подчёркивания, точки и символа @. Понятие атома заимствовано из Пролога и его можно считать аналогом перечислений (enum) в других языках программирования (без необходимости предварительной декларации). Атомы используются почти исключительно в сравнениях, имеющих в Erlang очень эффективную реализацию. Кроме того, некоторые атомы имеют определённый смысл в возращаемых значениях и описании исключений. К ним относятся: error, ignore, noreply, ok, reply, stop, undefined.
Битовые строки и бинарные данные
Битовая строка используется для хранения в памяти нетипизированных данных. Строки, состоящие из целого количества октетов, называются бинарными (или двоичными) данными (англ. binaries). Синтаксис описания битовой строки довольно гибок, так как описывает значения битов отдельных диапазонов и может быть снабжён модификатором типа. Несколько примеров в интерактивной командной оболочке:
1> <<23,89,120>>.
<<23,89,120>>
2> <<"ABC">>.
<<65,66,67>>
3> <<10,17,42:16>>.
<<1,17,0,42>>
4> <<$a, $b, $c>>.
<<"abc">>
5> <<1024/utf8>>.
<<208,128>>
Выражения битовых строк (англ. bitstring comprehension) аналогичны списковым включениям, но работают над битовыми строками:
1> << <<bnot(X):1>> || <<X:1>> <= <<2#111011:6>> >>.
<<4:6>>
В этом примере переменная X последовательно получает биты числа 2#111011, которые затем инвертируются операцией битового отрицания bnot (от англ. binary NOT), в результате чего получается число 4.
Кортеж
Кортеж (англ. tuple) - составной тип данных с фиксированным количеством элементов. При доступе к элементам кортежа с помощью встроенных функций нумерация элементов начинается с единицы, а не с нуля. Первый элемент кортежа принято использовать для указания роли кортежа в программе. Если первый элемент - атом, его называют тегом (англ. tag - «метка»). В Erlang принято строить различные типы данных на основе кортежей с тегами, что облегчает отладку программы и считается хорошим стилем программирования.
Для работы с кортежами есть несколько встроенных функций, например:
1> tuple_size({a, 1, "777"}).
3
2> element(1, {b, 2, 3, 4}).
b
3> setelement(1, {c, 5}, d).
{d,5}
Список
Список (англ. list) - составной тип данных, содержащий переменное число элементов. Для манипуляции со списками можно применять функции модуля lists стандартной библиотеки. Формально список определяется как имеющий голову (англ. head) и хвост (англ. tail), что выражается синтаксически в виде [HEAD|TAIL], где хвост обычно является списком (возможно, пустым). Пустой список обозначается [].
Списки можно записывать и более привычным способом. Следующие записи эквивалентны:
1> [a|[b|[c|[]]]].
[a,b,c]
Для работы со списками можно применять списковые включения (генераторы списков), например:
1> [ X / 2 || X <- [1,2,3,4]].
[0.5,1.0,1.5,2.0]
Модуль lists стандартной библиотеки содержит функции для обработки списков (и строк, так как в Erlang строка является списком), такие как нахождение максимума, сортировка, изменение порядка элементов на противоположный, суммирование элементов и т. п. В следующем примере два списка склеиваются операцией конкатенации, а затем разбиваются на две части функцией lists:split/2:
1> lists:split(2, [l,2,3]++[4,5,6]).
{[l,2],[3,4,5,6]}
В модуле 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 / A end. % функция D - деление V на A
#Fun<erl_eval.12.82930912>
2> lists:foldr(D, 1, [1, 2, 4, 8]).
0.25
3> 1/(2/(4/(8/1))).
0.25
Результат выполнения свёртки справа налево (в строке 2) тождественен цепочечному делению (строка 3). Второй параметр foldr даёт начальное значение для так называемого аккумулятора. Для каждого элемента списка (справа налево) к элементу и аккумулятору применяется функция, заданная первым аргументом foldr, а значение записывается в аккумулятор. По исчерпанию списка функция возвращает значение аккумулятора. Функция является достаточно мощным средством, если учесть, что аккумулятор может быть списком или кортежем.
Строка
В Erlang нет самостоятельного типа для строк: внутренне строки представляются списками. Синтаксически строку можно задать кавычками. Так, "Привет!" равносилен (в подходящей кодировке) списку [1055,1088,1080,1074,1077,1090,33]. Erlang поддерживает Unicode как в строке, так и в записи отдельного знака (через $).
Атомы и строки внешне достаточно похожи, но имеют совершенно различные реализации. Тогда как атомы можно только сравнивать, строки поддерживают многие другие операции, для них есть множество функций в модулях lists и string. Строка может выполнять функции атома, но память, занимаемая строкой пропорциональна её длине, тогда как атомы хранятся в системной таблице и на каждое использование атома в программе приходится лишь пара байтов, вне зависимости от длины атома. Сравнение двух атомов - это сравнение двух внутренних идентификаторов, выполняемое за одну операцию, тогда как сравнение строк предполагает поэлементный проход элементов строк.
Логические значения
Для значений истина и ложь в Erlang применяются атомы true (истина) и false (ложь), которые и используются операциями сравнения, логическими операциями, встроенными функциями. Пример:
1> 2 < 3.
true
2> is_boolean(125).
false
Функциональный объект (Fun)
Fun-выражение позволяет создать анонимную функцию, например, для передачи в качестве параметра другим функциям. С помощью fun можно также получить функциональный объект для функции из модуля. Примеры:
1> lists:map(fun(X) -> X + 1 end, [1, 2, 3]).
[2,3,4]
2> Belongs = fun lists:member/2.
#Fun<lists.member.2>
3> Belongs(a, [a, b]).
true
Запись
Чтобы помечать отдельные элементы кортежей и избежать ошибок при написании программы, в Erlang был внесён синтаксис записей (англ. record). Для работы с записями, необходимо в начале дать описание записи директивой -record, например, для записи user описание может быть следующим:
-record(user, {login="anon", password, nickname}).
Из этого описания компилятор узнаёт, что имеются в виду кортежи из четырёх элементов, в которых элементы со второго по четвёртый соответствуют полям login, password, nick (порядок важен) записи с именем user (определяется атомом в первом элементе кортежа). Значением по умолчанию для поля name является строка "anon". Если значение по умолчанию не указано явно, подразумевается атом undefined.
Создание записей и извлечение элементов записи всегда требует явного указания имени записи:
R0 = #user{} % все поля получают значения по умолчанию
R1 = #customer{login="user1", password="secret", nick="john"} % все поля получили значения
Синтаксис доступа к значениям полей записи: R1#user.login, R0#user.nick.
Другие типы
В языке Erlang имеются и другие типы данных. Тип ссылка (англ. reference) является практически уникальной в среде времени выполнения Erlang. Ссылка создаётся вызовом функции make_ref/0 и может повториться через 2 вызовов этой функции. Ссылки можно сравнивать на равенство, а применяются они для одноразовых пометок или «волшебного печенья».
Идентификатор порта (англ. port identifier) определяет порт для связи с внешним по отношению к Erlang-системе миром. Порт позволяет создавшему его процессу-владельцу (так называемому присоединённому процессу) обмениваться бинарными сообщениями со сторонними программами и ОС способом, принятым в данной операционной системе.
Идентификатор процесса (англ. Pid), как и следует из его названия, идентифицирует процесс, порождаемый различными функциями spawn. Идентификатор можно считать уникальным во время работы Erlang-системы, но в долго работающих системах могут всё-таки быть использованы повторно, что обычно не является проблемой на практике.
Встроенные функции для работы с типами
Для преобразования типов используются встроенные функции (BIF, от англ. builtin function) вида x_to_y («из x в y»), а для проверки принадлежности значения тому или иному типу - функции вида is_x («является x»):
1> atom_to_list(hello).
"hello"
2> list_to_binary("world").
<<"world">>
3> tuple_to_list({1,2,3,4}).
[1,2,3,4]
1> is_integer(3).
true
2> is_tuple("abc").
false
3> is_integer(3).
true
Операции
Арифметические операции
Erlang предоставляет наиболее распространённые арифметические операции для целых чисел и чисел с плавающей запятой:
Обозначение Выполняемая операция Пример Результат примера
+ Унарный плюс +3 3
- Унарный минус -3 -3
+ Сложение 2+3 5
- Вычитание 7-3 4
* Умножение 1.2*0.4 0.48
/ Деление 5 / 3 1.6666666666666667
div Целочисленное деление 5 div 3 1
rem Целочисленное деление с остатком 5 rem 3 2
Все эти операции левоассоциативны. Унарные операции имеют наивысший приоритет, затем следует умножение и деление, наименьший приоритет у сложения и вычитания. При необходимости целое может приводиться к типу с плавающей запятой.
Битовые операции
Битовые операции работают над целыми числами и дают в результате целое число.
Обозначение Выполняемая операция Пример Результат примера
bnot Побитовое отрицание bnot (2#1000) -9
band Побитовое И 2 band 3 2
bor Побитовое ИЛИ 1 bor 2 3
bxor Побитовое исключающее ИЛИ 5 bxor 3 6
bsr Побитовый сдвиг вправо 32 bsr 2 8
bsl Побитовый сдвиг влево 1 bsl 5 32
Логические операции
Логические операции работают над логическими значениями true (истина) и false (ложь), получаемыми в результате сравнений и применения функций проверки типа.
Обозначение Выполняемая операция Пример Результат примера
not Отрицание (НЕ) not true false
and Конъюнкция (И) true and (1 < 5) true
andalso Аналогично and, но не вычисляет второй операнд, если первый false false and (1 < 5) false
or Дизъюнкция (ИЛИ) is_atom("1") or is_atom(1) false
orelse Аналогично or, но не вычисляет второй операнд, если первый true true and (1 < 5) true
xor Исключающее ИЛИ true xor true false
Операции сравнения
Операции сравнения получают два операнда, а результатом операции является логическое значение true или false. В Erlang есть следующие операции: == (равно), /= (не равно), =< (меньше или равно), < (меньше), > (больше), а также сравнения, которые работают без приведения к одному типу: =/= (не равно в точности) и =:= (равно в точности).
Можно сравнивать и значения разных типов, но они считаются в Erlang упорядоченными следующим образом:
число < атом < ссылка < функция < порт < идентификатор процесса < кортеж < список < бинарные данные
Списки считаются упорядоченными в лексикографическом порядке, а кортежи сравниваются по длине, и только затем в лексикографическом порядке.
Переменные
Переменные служат для хранения значений простых и составных типов. Имя переменной начинается с прописной буквы (в специальных случаях - с подчёркивания) и может содержать буквы, цифры, подчёркивания. Значение можно присвоить переменной лишь один раз - это свойство языка программирования называется единичным присваиванием (англ. single assignment). К достоинствам единичного присваивания можно отнести устранение необходимости в блокировках, а также упрощение отладки программы.
Передача параметров в функцию происходит по значению, поэтому все они вычисляются перед вызовом функции.
Область видимости переменной распространяется от момента её появления в заголовочной части описания функции или присваивания до конца части описания функции. Пример:
binomial(X) -> Y = X*X, X + Y;
prod([1|T]) -> prod(T),
prod([Y|T]) -> Y * prod(T),
prod([]) -> 1.
В этом примере область видимости X - всё описание функции binomial/1, а Y - от присваивания до конца описания. Переменная Y во второй части (клоза) описания функции prod/1 не имеет отношения к переменной Y из binomial/1: её область видимости распространяется до конца этого клоза.
При выходе вычислений за пределы области видимости переменной память, занятая её содержимым, может быть освобождена в процессе сборки мусора, если значение переменной не используется в другой части программы.
Сопоставление с образцом
Сопоставление с образцом используется в Erlang для присваивания (в том числе, при работе с параметрами функций), управления потоком выполнения программы, извлечения значений составных типов, выбора сообщения из очереди. В левой части сравнения (или в заголовке функции) могут находиться связанные (уже имеющие значение) и несвязанные (получающие значение) переменные, а также литералы (атомы, числа, строки). В результате исполнения сравнение может оказаться успешным (в этом случае переменные связываются со значениями) и неуспешным - переменные остаются несвязанными. В образце могут быть переменные, значение которых для образца безразлично: их имена записываются начинающимися с подчёркивания. Переменная с именем _ (подчёркивание) сопоставляется с любым значением, но при этом не происходит связывания. Такую переменную можно применять много раз.
Функции
Программы на Erlang состоят из функций, которые вызывают друг друга. Количество параметров функции называется арностью. При вызове функции заголовочные части описания функции сопоставляются с образцом. В случае совпадения параметров вызова, формальные параметры связываются с фактическими и исполняется соответствующая часть тела функции. Запись варианта вычисления функции для некоторого образца может называется клозом от англ. clause, а определение функции - это набор из одного или более клозов.
Для уточнения сопоставления с образцов в функциях можно использовать охранные выражения, которые следуют после ключевого слова when. В примере ниже определена функция вычисления знака числа, которая рассчитывается в зависимости от сравнения параметра с нулём:
sign(X) when X > 0 -> 1;
sign(X) when X == 0 -> 0;
sign(X) when X < 0 -> -1.
Клозы Erlang перебирает в том порядке, в котором они записаны, пока не будет найден подходящий заголовок. В охранных выражениях можно использовать только ограниченный набор встроенных функций, так как эти функции не должны иметь побочных эффектов.
Разумеется, функции Erlang поддерживают рекурсивные вызовы. В случае, когда определение функции оканчивается рекурсивным вызовом (хвостовая рекурсия), Erlang использует оптимизацию: стек вызовов не применяется.
Как параметром, так и результатом функции может быть другая функция. В следующем примере функция одного аргумента возвращает функцию для прибавления аргумента:
1> Plus = fun (X) -> fun(Y) -> X+Y end end. % Определение функции, возвращающей функцию
#Fun<erl_eval.6.82930912>
2> Plus(2). % Функция возвращает Fun-объект
#Fun<erl_eval.6.82930912>
3> Plus(2)(3). % Такой синтаксис не работает
* 1: syntax error before: '('
4> (Plus(2))(3). % Дополнительные скобки позволяют добиться требуемого результата
5
5> Plus2 = Plus(2), Plus2(3). % То же самое с использование дополнительной переменной
5
Примеры
Вычисление факториала на Erlang:
-module(fact).
-export([fac/1]).
fac(0) -> 1;
fac(N) when N > 0, is_integer(N) -> N * fac(N-1).
Алгоритм сортировки, напоминающий быструю сортировку:
-module(qsort).
-export([qsort/1]).
qsort([]) -> []; % Тривиальный случай пустого списка
qsort([Pivot|Rest]) ->
% Конкатенация списка элементов до Pivot, списка из одного элемента Pivot и после Pivot
qsort([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-выражения. Выражение выбора позволяет организовать сопоставление с образцом внутри функции и обычно имеет следующий синтаксис:
case выражение-выбора of
образец1 when охрана1 -> выражение11, выражение12, ...;
образец2 when охрана2 -> выражение21, выражение22, ...;
...
образецN when охранаN -> выражениеN1, выражениеN2, ...
end
Это выражение всегда возвращает значение, соответствующее последнему вычисленному выражению в строке с подошедшим образцом. Это возвращаемое значение может служить возвращаемым значением функции, а может быть присвоено переменной. Как и в заголовочной части функции, после образца может следовать охранное выражение.
Упрощённым вариантом case-выражения является if-выражение:
if
охрана1 -> выражение11, выражение12, ...;
охрана2 -> выражение21, выражение22, ...;
...
охранаN -> выражениеN1, выражениеN2, ...
end
Здесь охранаi - охранное выражение. Первое истинное охранное выражение вызывает выполнение соответствующих выражений, последнее из которых и является значением всего if-выражение. Следует заметить, что и здесь в охранном выражении можно применять только ограниченный набор операций и встроенных функций.
Запятые в охранном выражении работают как операция and, например:
if
X =< 0 -> 'меньше или равно нулю';
X > 0, X < 10 -> 'больше нуля и меньше десяти';
X >= 10 -> 'больше или равно десяти';
end
Компилятор Erlang следит за безопасностью связывания переменных внутри условных выражений, как видно из следующего примера модуля:
-module(badexample).
-export([broken_if/1]).
broken_if(X) ->
if
X < 0 -> Z = -1;
X >= 0 -> Y = 1
end,
Y * Z.
При попытке откомпилировать модуль возникают сообщения об ошибках, так как в таком коде одна из переменных не связывается со значением:
1> c(badexample).
badexample.erl:8: variable 'Y' unsafe in 'if' (line 4)
badexample.erl:8: variable 'Z' unsafe in 'if' (line 4)
error
Правильным было бы определить все используемые далее по коду переменные во всех ветвях if-выражения.
Препроцессор и макросы
Препроцессор Erlang (EPP) позволяет вкладывать файлы с исходным кодом один в другой, определять макросы и осуществлять простые и параметризованные макроподстановки. Макрос определяется с помощью директивы -define, а макроподстановка осуществляется указанием имени макроса и возможных параметров после вопросительного знака (?). Следующий пример показывает определение и применение параметризованного макроса:
-define(ZERO(X),X == 0)
is_zero(T) when ?ZERO(X) -> true;
is_zero(T) -> false.
Имя макроса обычно пишется прописными буквами. Определение макроса должно содержать лексемы Erlang целиком (например, попытка задать часть имени переменной с помощью макроса вызовет синтаксическую ошибку). Макросы могут использоваться для повышения удобочитаемости кода в охранных выражениях, для операторов отладки и т. п. Препроцессор имеет несколько предопределённых макросов, которые нельзя переопределить: ?MODULE, ?MODULE_STRING, ?FILE, ?LINE, ?MACHINE.
Заголовочный файл (расширение .hrl) с определениями макросов и записей можно включить при помощи директивы -include.
Обработка ошибок
Для обработки исключительных ситуаций в Erlang можно применять конструкцию try-catch, в общем случае записываемую в следующем виде:
try вычисляемое-выражение of
образец1 when охрана1 -> выражение1;
образец2 when охрана2 -> выражение2;
...
образецN when охранаN -> выражениеN
catch
класс1:образецИскл1 when охранаИскл1 -> выражениеИскл1;
...
классM:образецИсклM when охранаИсклM -> выражениеИсклM;
end
Как и в случае case-выражения, вычисляемое выражение сопоставляется с образцом (части между of и catch) для получения результата. После ключевого слова catch следуют части обработки исключений, в которых в дополнение к образцам исключений могут быть указаны классы исключений (перед двоеточием): error, throw и exit. Подчёркивание может использоваться как в образце, так и в классе исключения. Следующий простой пример иллюстрирует перехват ошибки класса error при вычислении квадратного корня:
1> try math:sqrt(-1) catch error:Error -> {error, Error} end.
{error, badarith}
2> try math:sqrt(4) catch error:Error -> {error, Error} end.
2.0
Для создания исключений, определённых пользователем, используется функция throw/1, которая принимает кортеж с более детальным описанием возникшей ошибки и генерирует исключение класса throw. Использование этой функции нежелательно из-за ухудшения удобочитаемости кода программы, но может потребоваться в некоторых случаях при работе с вложенными структурами данных, например, при разборе XML. Исключения класса exit возникают в результате вызова встроенной функции exit/1 или сигнала выхода.
До разработки Ричардом Карлссоном (Richard Carlsson) из команды проекта HiPE описанного выше нового механизма обработки исключений (появился в версии R10B) в Erlang использовались catch-выражения.
Модули
Код программы на Erlang можно разбить на отдельные модули. Модуль - имя для набора функций, организованных в одном файле. Имя модуля должно совпадать с именем файла (если отбросить расширение). Модуль можно откомпилировать в байт-код как из командной строки операционной системы, так и из командной оболочки Erlang. В файле модуля можно записать объявления функций и директивы (иногда называются атрибутами). Обязательным атрибутом является только -module(атом_имени_модуля). Другой часто используемый атрибут - -export - применяется для указания списка экспортируемых функций, то есть функций, которые можно использовать за пределами модуля.
Функции в Erlang однозначно определяются модулем, именем и арностью. Например, math:cos/1 соответствует функции cos из модуля math, принимающей один аргумент. Вызвать функцию можно так: math:cos(1.2).
Исходный текст модуля компилируется в BEAM-файл - файл, содержащий байт-код виртуальной машины BEAM (англ. Bogdan’s/Björn's Erlang Abstract Machine). В свою очередь, ERTS (англ. Erlang Runtime System - система времени выполнения Erlang) выполняет этот код.
Процессы
Основной абстракцией параллельного программирования в Erlang является процесс. Процессы могут порождать другие процессы, выполняться одновременно, обмениваться сообщениями, реагировать на завершение друг друга.
Создание процессов
Для создания нового процесса служит несколько встроенных функций (spawn и её аналоги). Функции возвращают идентификатор процесса, который может использоваться, например, для отправки сообщений вновь созданному процессу. В интерактивной консоли erl можно получить список процессов и другую информацию посредством вызова функций processes(). и i(). соответственно.
Отправка и приём сообщений
Как и язык Оккам, Erlang использует для отправки сообщения синтаксис с восклицательным знаком: ИдПроцесса ! Сообщение. Приём сообщения - то есть извлечение его из очереди («почтового ящика») процесса - выполняется с помощью receive-выражений, обычно записываемых следующим образом:
receive
образец1 when охрана1 -> выражение11, выражение12, ...;
образец2 when охрана2 -> выражение21, выражение22, ...;
...
образецN when охранаN -> выражениеN1, выражениеN2, ...;
НесвязаннаяПеременнаяДляОстальныхСообщений -> выражение1, выражение2, ...
end
Встретив такое выражение, интерпретатор последовательно просматривает сообщения из очереди. Каждое сообщение интерпретатор сопоставляет с образцом и, если оно удовлетворяет образцу, вычисляются соответствующие выражения. Когда все сообщения перебраны, и подходящего не оказалось, процесс блокируется в ожидании новых сообщений, после чего перебор очереди повторяется. Если в receive-выражении отсутствует образец, которому удовлетворяет любое сообщение, такое выражение называется выборочным receive-выражением.
Обработка ошибок и завершение процессов
Процесс можно связать с другим, в результате чего между процессами устанавливается двунаправленное соединение (англ. link). В случае, если один из процессов завершается ненормально, всем связанным с ним процессам передаётся сигнал выхода (англ. exit signal). Процессы, получившие сигнал, завершаются, распространяя сигнал дальше. Сигнал выхода является кортежем, элементами которого являются атом 'EXIT' (выход), идентификатор завершившегося процесса и причину завершения процесса. Причина завершения передаётся по цепочке завершающихся процессов.
Процесс может осуществить перехват ошибки (англ. trapping errors), если у него установлен флаг перехвата выхода. Такой процесс получает сигналы выхода связанных с ним процессов в виде обычных сообщений с той же структурой кортежа. Перехваченный сигнала выхода более не передаётся связанным с процессом-перехватчиком процессам. Сигнал выхода с причиной - атомом normal (нормальное завершение процесса) не вызывает завершения связанного процесса. Если же причина - атом kill, процесс завершается безусловно (независимо от флага перехвата выхода), а связанным с ним процессам в качестве причины отправляется атом killed, что даёт им возможность среагировать.
В Erlang есть возможность установить и однонаправленное соединение. При завершении наблюдаемого процесса процесс-наблюдатель получает сообщение с указанием причины завершения.
Процесс может остановить сам себя или другой процесс, вызвав функцию exit.
Ввод-вывод
В планировщике процессов Erlang-системы проблема ввода-вывода, присущая многим другим языкам параллельного программирования, решена достаточно элегантно. Управление вводом-выводом, интегрированное с планировщиком, уже на самом нижнем уровне осуществляется на основе событий, что позволяет программе обрабатывать входящие и исходящие данные без излишних блокировок. Такой подход требует меньшего числа установки и разрыва соединений, а также убирает необходимость в блокировках и переключениях контекста. К сожалению, такой достаточно эффективный способ более сложен для понимания программистами, и находит применение в основном в системах с явными требования по высокой доступности и низкому времени отклика. Реализация событийно-ориентированного ввода-вывода встроена в Erlang-систему, что является ещё одним преимуществом при проектировании параллельных приложений.
Стандартная библиотека содержит модуль io с функциями ввода-вывода. Такие функции содержат побочные эффекты, заключающиеся в появлении выведенной информации на консоли или записывании данных в файл на диске. Например, функция io:format для форматированного вывода выводит строку с подстановкой параметров, возвращая в случае успеха атом ok:
1> io:format("Пример вывода: ~p~n", [1]).
Пример вывода: 1
ok
Функции модуля io включают в себя стандартный серверный интерфейс ввода-вывода. Протокол ввода-вывода Erlang (англ. The Erlang I/O-protocol) детально определяет связь клиента и сервера. Под сервером ввода-вывода понимается процесс, который обрабатывает запросы и выполняет запрошенные команды, например, на устройстве ввода-вывода. Клиентом является любой Erlang-процесс, которому требуется работать с устройством.
Библиотеки
Стандартная библиотека модулей
Согласно официальной документации, стандартная библиотека модулей STDLIB является обязательной для включения в минимальную систему Erlang/OTP наряду с ядром Erlang. В библиотеку входят модули, предоставляющие разнообразные функции для работы со встроенными типами и другими структурами данных, ввода-вывода, обращения к среде окружения, для работы с файловой системой, процессами и т. п.
Модуль array определяет (функциональный) абстрактный тип данных для динамического массива и имеет функции, позволяющие извлекать и обновлять элементы массива, определять рекурсивные функции для работы с массивами. Модуль string расширяет возможности модуля lists функциями для работы конкретно со списками символов, какими являются строки в Erlang. Модуль dict (от англ. dictionary - словарь) содержит функции для ассоциативного массива, позволяющие хранить, извлекать и удалять значения по ключу, соединять массивы и производить итерации по элементам. Математические функции можно найти в модуле math, а функции для генерации псевдослучайных чисел содержатся в модуле random. Модуль calendar предоставляет функции для григорианского календаря: запросы текущей даты, преобразования единиц измерения и интервалов времени, а модуль timer содержит функции перевода интервалов времени к миллисекундам, запуска событий по таймеру и другие, связанные со временем, функции. Модуль erlang содержит все встроенные функции Erlang, как общие, так и относящиеся к виртуальной машине. Модуль file даёт доступ к функциям файловой системы, таким как открытие, чтение, запись, удаление файлов, а модуль filename позволяет писать функции для манипуляции с именами и путями к файлам, абстрагируясь от конкретной операционной системы. Модуль io предоставляет функции ввода-вывода. Кроме этих наиболее важных модулей, стандартная библиотека содержит и многие другие, с которыми можно познакомиться по документации.
Таблицы ETS и DETS
Для организации коллекций в оперативной памяти Erlang предлагает модуль ets (ETS, англ. Erlang Term Storage - «хранилище термов Erlang»). ETS может хранить четыре вида коллекций: множество (англ. set), упорядоченное множество (англ. ordered set), мультимножество (англ. bag), мультимножество с повторениями (англ. duplicate bag). Доступ к элементам коллекций происходит по ключевому полю кортежа (ключи могут быть любых типов). Упорядоченные множества реализованы в виде бинарных сбалансированных АВЛ-деревьев, а остальные коллекции - с использованием хэш-таблиц.
DETS-таблицы дополняют функциональность ETS-таблиц (за исключением упорядоченных множеств), позволяя сохранять данные в файлах.
Фреймворк OTP
Основная статья: Open Telecom Platform
Erlang
Дерево процессов
OTP (англ. Open Telecom Platform) является хорошо отлаженным набором полезных поведений (англ. behaviours) процессов и используется для создания серверных приложений. OTP формализует действия процессов и позволяет строить на их основе OTP-приложения (не следует путать с приложением - готовым программным продуктом). В модулях ОТР определены общие, стандартизированные шаблоны для конструирования параллельных приложений. Наиболее популярными поведениями являются обобщённый сервер и наблюдатель (англ. supervisor), но имеются и другие: конечный автомат, обработчик событий. OTP содержит и другое связующее программное обеспечение (англ. middleware), например, СУБД Mnesia.
OTP-поведения делятся на рабочие процессы (англ. worker processes), выполняющие собственно обработку запросов, и процессов-наблюдателей (англ. supervisors). В задачу последних входит слежение за рабочими процессами и другими процессами-наблюдателями - потомками. Деревья наблюдателей составляют OTP-приложение (англ. application). Документация по Erlang определяет OTP-приложение как компонент, реализующий некоторую функциональность, которая может быть независимо запущена на исполнение и остановлена как целое, а также повторно использована в других системах. Разработчик приложения пишет код модулей функций обратного вызова (англ. call-back module), в которых и находится специфичная для данного приложения часть функциональности.
Хотя OTP строго говоря не является частью языка Erlang, он настолько вошёл в культуру и практику разработчиков на Erlang, что подчас между ними сложно провести границу.
Разработка графического интерфейса пользователя
Разработка приложений с графическим интерфейсом пользователя (не считая веб-интерфейсов) может вестись при помощи библиотеки wxErlang - библиотеки wxWidgets, портированной для Erlang. WxErlang входит в стандартную поставку Erlang/OTP. WxWidgets написан на C++, поэтому перед разработчиками wxErlang стояла задача выразить средствами Erlang иерархию объектов. Несколько упрощая, в wxErlang классам соответствуют модули, а объектам - ссылки. Макросам на С++ соответствуют макросы Erlang. Некоторые типы данных, для которых в С++ были использованы классы, представляются в Erlang с помощью других типов данных, например, wxPoint задаётся в виде кортежа из двух элементов. События в wxErlang могут быть обработаны в Erlang либо с помощью функций обратного вызова (англ. call-back functions), либо более естественной в среде Erlang передачей сообщений.
Программирование на Erlang
Интерактивная оболочка
Erlang
Интерактивная оболочка erl в отдельном окне
Интерактивная оболочка (англ. shell) для Erlang может быть вызвана в Unix-подобных системах по команде erl, в Windows - werl. В оболочке можно вводить выражения и получать результат их выполнения, опробовать новый код, заниматься интерактивной отладкой, а также управлять системой, находящейся в промышленной эксплуатации.
В оболочке можно использовать дополнительные функции («команды»), доступные только в ней. Например, команда q(). осуществляет выход из оболочки с завершрением всего, что делает Erlang-система.
В оболочке можно вызвать BREAK-меню с помощью Ctrl + C (в Unix-подобных ОС) или Ctrl + Break (в Windows). В этом меню есть различные команды, в том числе a - немедленный останов, c - продолжение работы в оболочке и другие информационные и вспомогательные команды для работы с Erlang-системой. Комбинацией клавиш Ctrl + G вызывается ещё одно командное меню, с помощью которого можно, среди прочего, остановить «завесивший» оболочку процесс и вернуться в оболочку ( i и затем c ).
Документирование и оформление кода
Текст от знака процента (%) до конца строки считается комментарием в Erlang. Генерация документации из исходного кода в Erlang может производиться системой документирования EDoc. Для документирования кода модуля достаточно добавить определённым образом размеченный текст, а также файл overview.edoc для документации уровня проекта (в последнем необязательно использовать знаки комментария). Инструменты для работы с кодом на Erlang, например, erlang-режим в Emacs, подразумевают некоторые соглашения по употреблению символов комментария. Так, утроенный знак процента вызывает выравнивание по левому краю, удвоенный - выравнивание на уровне окружающего кода, а одиночный знак процента используется для обозначения комментария после кода, в конце строки. Разработчики Erlang выработали определённые стилевые соглашения, касающиеся организации и оформления исходного кода. Например, хорошим стилем считается понижение вложенности синтаксических структур, написание коротких модулей (менее 400 строк кода) и функций (не длиннее 15-20 строк кода), использование осмысленных имён для переменных и функций и т. п.
Типы и анализ кода
Приложение Dialyzer, разработанное в рамках проекта HiPE и входящее в стандартную поставку, позволяет выявить ошибки (в том числе ошибки типизации) путём статического анализа кода. Программа TypEr, написанная Тобиасом Линдалом (Tobias Lindahl) и Костисом Сагонасом (Kostis Sagonas), является частью Dialyzer. Эта программа позволяет проверять определения типов функций, сверять указанный в директиве -spec тип функции с её определением, выполнить вывод типов. Программа TypEr выводит все типы, соответствующие успешному применению функции, в общем случае - лишь приблизительно, в более грубую сторону. Использование функции любым другим способом обязательно приведёт к ошибке времени исполнения. В следующем примере показан синтаксис определения типа (директива -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 устанавливает тип, используя основанный на ограничениях вывод типов и анализ потоков данных. После определения типов функций производится анализ противоречий в программе.
Тестирование, профилирование, рефакторинг
Erlang предоставляет EUnit для модульного тестирования и фреймворк Common Test для системного тестирования. EUnit содержит средства для описания тестов, включая необходимый для этого набор макросов, а также производит вывод отчёта по окончании тестирования. Тестирование модулей происходит путём подключения заголовочного файла из EUnit, а функции с тестами могут быть как включены в сам тестируемый модуль, а так и вынесены в отдельный.
Тестирование параллельных программ можно выполнить с помощью Quviq Quick Check (версия Mini этого продукта доступна бесплатно). Кроме тестирования, можно провести проверку всех возможных вариантов исходных данных с помощью метода проверки моделей. Для этого можно воспользоваться созданной в Мадридском политехническом университете отдельно распространяемой утилитой McErlang.
Для профилирования кода и выявления степени покрытия кода тестами можно обратиться к модулям eprof, fprof, cover и утилите cprof.
Для Erlang разработаны несколько инструментов рефакторинга исходного кода, такие как RefactorErl, Wrangler, а также автоматическая, независимая от IDE утилита tidier. Утилита tidier позволяет автоматически находить и производить эквивалентные преобразование кода, например, заменяет lists:filter(fun (X) -> is_something(X) end, L) на [X || X <- L, is_something(X)].
Эффективность
Как и многие другие языки программирования, Erlang имеет свои секреты написания эффективного кода. Совершенствование языка делает некоторые из трюков устаревшими, поэтому документация является лучшим руководством в вопросах оптимизации, в совокупности с профилированием и стресс-тестированием.
Например, при работе со списками не рекомендуется добавлять элемент в конец длинного списка с помощью конкатенации или функции добавления элемента к списку. Вместо этого стоит рассмотреть возможность добавления элемента в начало списка, а конечный результат обработать функцией обращения порядка элементов списка.
Свои рекомендации есть и для увеличения эффективности параллельных программ. Например, действия, требующие много памяти, лучше всего выделять в отдельный процесс, так как при этом затраты на сборку мусора будут минимальны: память будет освобождена по завершению процесса.
Erlang и другие языки программирования
Подробное сравнение возможностей Erlang с другими языками можно найти в статье сравнение языков программирования.
Интеграция и гибридные языки
Erlang-система позволяет выполнять интеграцию с системами на других языках программирования. Имеются механизмы для сетевого взаимодействия с Си, Java, Лисп, Perl, Python, Ruby. Например, для более эффективного синхронного вызова небольших функций на Си можно использовать платформно-зависимые функции (англ. NIF, natively implemented function). Высокоуровневые библиотеки позволяют Erlang-системе представлять С или Java-узлы как обычные Erlang-узлы. Другие языки могут быть более тесно сопряжены со средой выполнения Erlang с помощью драйверов или сетевых сокетов посредством протоколов вроде HTTP, SNMP, IIOP. Например, Ruby может взаимодействовать с Erlang посредством пакета erlectricity, а для Python разработана реализация Erlang-узла в виде пакета py-interface.
Виртуальная машина Erlang находит применение и в других языках программирования, например, Elixir и проекте Erl2 Джо Армстронга. Кроме того, Роберт Вирдинг поддерживает проект Lisp Flavored Erlang («Erlang, приправленный Лиспом»), в котором синтаксис Лиспа используется с компилятором Erlang. Официальный сайт упоминает проект Erjang, в котором используется виртуальная машина Java.
Сравнение Erlang и C++ по производительности
Хотя опытные Erlang-программисты давно заметили, что их программы для тех же задач получаются более краткими по сравнению с другими широко используемыми в промышленности языками программирования, эмпирическое исследование показало, что для изученных телекоммуникационных приложений код на Erlang был на 70-85 % короче, чем на С++, а производительность системы при переписывании кода с С++ на Erlang возросла почти на 100 %. Для одного из использованных в исследовании проектов разница была объяснена написанием дополнительного С++-кода в рамках защитного программирования, управления памятью и кода для высокоуровневой коммуникации, то есть возможностями, которые являются частью языка Erlang и библиотек OTP.
Сравнение взаимодействия процессов в Erlang и Go
Влияние теории взаимодействующих последовательных процессов Чарльза Э. Хоара чувствуется как в Go, так и в Erlang. В Erlang процессы, в соответствии с моделью акторов, отправляют сообщения друг другу напрямую. В Go то же самое происходит посредством каналов (англ. channels). Другим отличием является то, что каналы в Go имеют типы. В Erlang же нет типизации времени компиляции за исключением охранных выражений, что позволяет посылать процессам сообщения любого типа, но «непонятое» сообщение либо будет проигнорировано, либо навсегда останется в очереди. Go позволяет легко организовать группу «go-программ» (англ. goroutine - намёк на англ. co-routine - сопрограмма) для получения сообщений из некоторого канала (такой подход известен как пул потоков). В Erlang, при проектировании которого уделялось особое внимание детерминизму и времени задержки (англ. latency), реализация рабочего пула возможна, но требует дополнительных усилий. Множественные отправители тривиально реализуются в обоих языках. Erlang-процесс может послать сообщение и ждать на него ответ (соответствующий некоторому образцу), игнорируя другие сообщения в очереди. В Go такое невозможно, но подобная функциональность может быть достигнута созданием (в том числе, динамическим) новых вводов, то есть разделением каналов по назначению. Go требует явного указания того, какие go-программы будут взаимодействовать с другими передачей сообщений, тогда как в Erlang отсутствует разделяемое между процессами изменяемое состояние (англ. shared mutable state) и поэтому изолированный процесс очень редко представляет интерес.
Абстракции взаимодействующих процессов достаточно похожи в Erlang и Go, однако во избежание ошибок при переходе с одного языка на другой следует учитывать нюансы: шаблоны, которые хороши в одном языке, могут не подходить для другого.
Критика
Как и любой язык программирования, Erlang не свободен от недостатков. К погрешностям синтаксиса можно отнести зависимость от символа окончания выражения от контекста (это может быть ., , или ;), что требует дополнительного внимания при перемене выражений местами, излишнюю многословность записей (тип записи приходится упоминать при каждом доступе к члену записи), необходимость полного перечисления альтернатив в if-выражении во избежание выбрасывания исключения, если ни одно из условий не выполнено. К недостаткам можно отнести строго ограниченный набор функций, которые можно использовать в if-выражениях (этот недостаток можно обойти использованием case-выражений). Функциональный стиль и неизменяемые переменные приводят в некоторых приложениях (например, тесты) к большему количеству правок, чем в других языках программирования, так как вставка некоторой промежуточной обработки может потребовать новых имён переменных, что может привести к изменениям в коде, следующем далее по тексту. Из недостатков системы типов можно указать отсутствие строкового типа, а также невозможность динамически добавлять в записи новые члены. Есть проблемы и с организацией исходного кода, которая возможна только через создание нового файла, а также отсутствие пространств имён, классов или других средств для организации кода. Уровень качества модулей, за исключением основных, и документации оставляет желать лучшего.
Один из создателей языка, Джо Армстронг, в своём выступлении на конференции по истории языков программирования в 2007 году перечислил список областей, в которых Erlang можно было бы улучшить:
Использование сборки мусора для атомов.
Улучшение средств сопряжения со внешним кодом (англ. foreign code).
Усиление изоляции между процессами.
Более избирательная система безопасности среди узлов Erlang, основанная на различной степени доверия.
Отдельные обозначения для протоколов и систем.
Модули должны быть объектами первого класса.
Массовое распространение Erlang может сдерживать необычный для большинства программистов синтаксис, использование функциональной парадигмы, а также то, что наилучшая на 2010 год реализация языка использует виртуальную машину BEAM, а не более распространённую JVM.
Сфера применения
Erlang
Типичная архитектура системы, использующей Erlang/OTP. Приложения Erlang пользуются службами Mnesia, SASL, агентами SNMP-мониторинга и другими на базе фреймворка OTP, который в свою очередь использует ERTS. Программы других систем программирования поддерживаются в меньшей степени.
В силу своих особенностей Erlang и существующих библиотек модулей Erlang подходит для создания сетевых серверов, распределённых систем, программ с GUI и подобных им интерактивных программ, инструментов для тестирования, управления и слежения, в общем, приложений с нерегулярным параллелизмом, в которых распараллеливаемые задачи достаточно разнообразны. Erlang не особенно хорош для написания кода, содержащего интенсивные вычисления с плавающей запятой, требующего включения нативного кода конкретной платформы или сильной оптимизации, а также для создания приложений, требующих синхронного параллельного выполнения задач. Не подходит Erlang и для проектов, в которых код должен исполняться на JVM или CLR, или проектов, требующих множества библиотек из других систем программирования.
Можно сказать, что Erlang стал применяться для разработки облачных систем ещё до того, как сформировалось само понятие облачных вычислений. Язык Erlang используется в масштабных телекоммуникационных и Интернет-приложениях многими компаниями, включая Amazon EC2 с реализацией SimpleDB, сервис социальных закладок Delicious, Facebook (бэкенд для чата), T-Mobile (сервис SMS и системы аутентификации).
Erlang часто ставят в заслугу легендарную надёжность ATM-коммутатора AXD301 (полтора миллиона строк кода на Erlang, полмиллиона - на C/C++) в сети British Telecom. По данным Ericsson, с момента установки в январе 2002 года за несколько лет случилась только одна незначительная неполадка, на основании чего надёжность системы по расчётам составила 99,9999999 %. Хотя более реальные оценки, учитывающие многие другие факторы, говорят всё-таки о «пяти девятках», успех маршрутизатора связывают с легкодоступными средствами разработки надёжных параллельных вычислений, встроенными в Erlang.
Используется Erlang и в приложениях с открытым исходным кодом, среди которых CouchDB - документо-ориентированная база данных с REST-интерфейсом, Disco - фреймворк для распределённых вычислений на основе парадигмы MapReduce, Ejabberd - свободный (GNU GPL), распределённый и устойчивый к отказам Jabber-сервер, написанный в основном на Erlang, RabbitMQ - платформа, ориентированная на обработку сообщений (реализация AMQP), Wings 3D - программа 3D-моделирования и другие.
Для Erlang были написаны несколько веб-серверов: Yaws (англ. Yet Another Web Server), Cowboy, а также MochiWeb - библиотека для создания HTTP-серверов. Кроме того, были созданы несколько веб-фреймворков и систем управления содержимым, таких как Nitrogen, Chicago Boss, Zotonic, а также более не разрабатываемые активно BeepBeep, Erlang Web, ErlyWeb.
Среди другого известного программного обеспечения, выполненного на Erlang, можно выделить распределённую NoSQL базу данных Riak, спроектированную по принципам Amazon DynamoDB, Flussonic (ранее известный как Erlyvideo) - видеостриминговый сервер, поддерживающий несколько протоколов. Для стресс-тестирования распределённых систем можно применять (также распределённый) написанный на Erlang инструмент Tsung, который позволяет эмулировать тысячи (при достаточном количестве тестовых серверов - миллионы) одновременных пользователей.
Erlang практически идеально подходят для задач искусственного интеллекта (особенно вычислительного интеллекта, нейроэволюции), основанных на нейронных сетях. Подобное применение возможно благодаря имеющимся у Erlang пяти ключевым свойствам «языка программирования нейронных сетей»: изолированные процессы-нейроны (англ. encapsulation), параллелизм (англ. concurrency, одновременность), механизм обнаружения сбоев, независимость от местоположения (англ. location transparency) и горячая замена кода. Примером такого применения является реализация одного из подходов к нейроэволюции - DXNN.
Сообщество
Вокруг технологий 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.
Примечания
Сайт проекта: The High-Performance Erlang Project. Uppsala University.
Вариант с of употребляется редко
Флаг trap_exit можно установить с помощью функции process_flag/2
Правильно сформированная структура данных любого типа называется термом.
Источники
Erlang OTP 17.0 has been released.
, с. 42
, 10.3 Examples of functional programming languages
, с. 25-26
, 6-14
, 6-16
, p. xxxi
, Backlash
, с. 26
, 6-17
, p. xxxii
, с. 24-25
Lars-Åke Fredlund. Erlang – a platform for developing distributed software systems. - Component Based Software: 2011-2012, Universidad Politécnica de Madrid.
, с. 381
EEP 0: Index of Erlang Enhancement Proposals (EEPs) (31-05-2007).
, с. 15
, p. 14
, с. vii
, с. 167
, p. 183
, с. 27
, с. 117
, с. 88
, с. 117-118
, с. 30-31
, с. 23-24
, с. 29
Joe Armstrong, Bjarne Däcker, Thomas Lindgren, Håkan Millroth, Erlang product team at Ericsson. Open-source Erlang - White Paper . Ericsson AB (2013).
, Types (or lack thereof)
, с. 39
, Type Specifications and Erlang
, с. 38-39
math, STDLIB Reference Manual Version 1.19.3 . Ericsson AB (2013).
, p. 31-32
, с. 41-42
, с. 172
, с. 230-235
, с. 233-234
, с. 44-45
, с. 48-51
, с. 47-48
, Starting Out (for real)
, с. 48-50
, с. 223
, с. 84-85
, с. 62
, с. 46
, с. 43
, с. 17
, p. 69-71
Data Types, Erlang Reference Manual User's Guide Version 5.10.3 . Ericsson AB (2013).
, с. 238
, p. 36
Ports and Port Drivers, Reference Manual User's Guide Version 5.10.3 . Ericsson AB (2013).
, с. 40-41
, с. 235-236
, с. 43-44
, с. 51-52
, с. 53-55
, p. 2-3
, с. 74
, p. 56
, с. 57-62
, с. 62-65
Душкин Р. Функциональное программирование на языке Haskell. - ДМК-Пресс, 2007. - С. 120. - 608 с. - купить книгу
, с. 30-31
, с. 32
, с. 51
, с. 220
, с. 71-73
, с. 75-76
, с. 42
, с. 44-45
, с. 193
, с. 194-195
The Preprocessor, Predefined Macros, Erlang Reference Manual User's Guide Version 5.10.3 . Ericsson AB (2013).
, с. 196-197
, с. 98
, с. 108
, с. 98-102
, с. 110
, с. 102
, с. 65-66
, Modules
, с. 18
, p. 75
, с. 120-124
, с. 118-120
, с. 167-168
, p. 139
, с. 168-169
, с. 170-171,175
, с. 176-177
, с. 172-174
, с. 174
, p. 19
, с. 83-84
The Erlang I/O-protocol, STDLIB User's Guide Version 1.19.4 . Ericsson AB (2013).
, p. 79
STDLIB Reference Manual Version 1.19.3 . Ericsson AB (2013).
STDLIB, STDLIB Reference Manual Version 1.19.3 . Ericsson AB (2013).
, с. 107-108
, с. 131
, с. 241-247
, с. 257
, с. 291
, с. 151
, с. 291-294
application, Kernel Reference Manual Version 2.16.3 . Ericsson AB (2013).
, с. 337-339,348
, с. 2-3
, p. 23-27
, Starting Out
, с. 20-21
, с. 21
Klas Eriksson, M. Williams, J. Armstrong. Programming Rules and Conventions. Ericsson AB.
, с. 460-465
, с. 424-430
, с. 442
, с. 449-450
, с. 449-450,478
, с. 166-167
, с. 470
, с. 471
, с. 31
, p. 125-128
elixir . Plataformatec (2013).
, с. 168
Joe Armstrong. erl2, a new dialect of erlang (2013).
Lisp Flavored Erlang .
Academic and Historical Questions . Ericsson AB (2013).
Welcome to Erjang! . Trifork A/S (2013).
, с. 35-36
David Chisnall. A Tale of Two Concurrency Models: Comparing the Go and Erlang Programming Languages . Pearson Education, Informit (14-11-2011).
Damien Katz (создатель CouchDB). What Sucks About Erlang (9 March 2008).
, 6-19,6-20
, p. 221
Ной Гифт. Работа с большими объемами данных в облаке с помощью MapReduce. IBM (01.03.2012).
, с. 23-25
, с. 33
Disco (страница проекта).
Martin Brown. Introduction to programming in Erlang, Part 2: Use advanced features and functionality . IBM (2013).
, p. 121-123
, с. 167
Flussonic - мультипротокольный видеостриминговый сервер. Flussonic, LLC.
Bradley Holt Chapter 6. Distributed Load Testing // Scaling CouchDB. - O'Reilly Media, Inc., 2011. - P. 39. - 72 p. - купить книгу
, Chapter 5 The Unintentional Neural Network Programming Language, pp. 144-150
, с. 168-169
Литература
на русском языке
Чезарини Ф., Томпсон С. Программирование в Erlang = Erlang Programming. - М.: ДМК Пресс, 2012. - 488 с. - купить книгу
на английском языке
Francesco Cesarini, Simon Thompson Erlang Programming. - O’Reilly Media, Inc., 2009. - 498 p. - купить книгу
Joe Armstrong Programming Erlang: Software for a Concurrent World. - Pragmatic Bookshelf, 2007. - 536 p. - купить книгу
Bruce A. Tate Seven Languages in Seven Weeks: A Pragmatic Guide to Learning Programming Languages. - Pragmatic Bookshelf, 2010. - купить книгу
Simon St. Laurent Introducing Erlang. - O’Reilly Media, Inc., 2013. - 185 p. - купить книгу
Zachary Kessin Building Web Applications with Erlang. - O’Reilly Media, Inc., 2012. - 156 p. - купить книгу
Martin Logan, Eric Merritt, Richard Carlsson Erlang and OTP in Action. - Manning, 2011. - 397 p. - купить книгу
Fred Hébert Learn You Some Erlang for Great Good!: A Beginner's Guide. - No Starch Press, 2013. - 624 p. - купить книгу
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. - купить книгу
Sher, Gene I. Handbook of Neuroevolution Through Erlang. - Springer, 2013. - 831 p. - купить книгу
Robert Virding, Claes Wikström, Mike Williams Concurrent Programming in ERLANG / J. Armstrong. - 2nd ed. - Prentice Hall International (UK) Ltd., 1996. - купить книгу
Maurice Castro Erlang in Real Time. - Department of Computer Science, RMIT, Australia, 2001. - купить книгу
Статьи
Дмитрий Васильев Знакомьтесь, Erlang // Системный администратор. - 2009. - № 8. - ISSN 1813-5579.
Joe Armstrong Erlang // Communications of the ACM. - 2010. - Т. 53. - № 9. - P. 68-75. - DOI:10.1145/1810891.1810910
Armstrong, Joe (January 2007). "A History of Erlang" in HOPL III. Proceedings of the Third ACM SIGPLAN Conference on History of Programming Languages: 6−1-6-26, San Diego, California: ACM. DOI:10.1145/1238844.1238850. 978-1-59593-766-7. .
Jim Larson Erlang for Concurrent Programming // ACM Queue. - 2008. - № 5. - P. 18-23. - DOI:10.1.1.207.3752
J. H. Nyström and P. W. Trinder and D. J. King High-level distribution for the rapid production of robust telecoms software: comparing C++ and ERLANG // Concurrency and Computation: Practice and Experience. - 2008. - Т. 20. - № 8. - P. 941-968. - DOI:10.1002/cpe.1223
Bjarne Däcker Concurrent Functional Programming for Telecommunications: A Case Study of Technology Introduction // Master thesis, Royal Institute of Technology. - Stockholm, 2000.
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
Пол Крил Функциональное программирование - друг параллелизма // Открытые системы. - 2010. - № 8.
Начала работы с Erlang = Getting Started With Erlang // RSDN Magazine. - 2006. - № 3. - ISSN 0234-6621.
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" in Erlang '12. Proceedings of the Eleventh ACM SIGPLAN Workshop on Erlang Workshop: 33-42, Copenhagen, Denmark: ACM. DOI:10.1145/2364489.2364495. 978-1-59593-766-7.