Русская Википедия:Erlang
Шаблон:Другие значения Шаблон:Карточка языка программирования
Erlang [[[:Шаблон:IPA]]][1] — функциональный язык программирования с сильной динамической типизацией, предназначенный для создания распределённых вычислительных систем. Разработан и поддерживается компанией Ericsson. Язык включает в себя средства порождения параллельных легковесных процессов и их взаимодействия через обмен асинхронными сообщениями в соответствии с моделью акторов.
Erlang был целенаправленно разработан для применения в распределённых, отказоустойчивых, параллельных системах реального времениШаблон:Переход, для которых, кроме средств самого языка, имеется стандартная библиотека модулейШаблон:Переход и библиотека шаблонных решений (так называемых поведений) — фреймворк OTP (Шаблон:Lang-en)Шаблон:Переход. Программа на Erlang транслируется в байт-код, исполняемый виртуальными машинами, находящимися на различных узлах распределённой вычислительной сети. Erlang-системы поддерживают горячую замену кодаШаблон:Переход, что позволяет эксплуатировать оборудование безостановочно.
Свой синтаксис и некоторые концепции Erlang унаследовал от языка логического программирования ПрологШаблон:Sfn. Язык поддерживает многие типы данныхШаблон:Переход, условные конструкцииШаблон:Переход, сопоставление с образцомШаблон:Переход, обработку исключенийШаблон:Переход, списковые включенияШаблон:Переход и выражения битовых строкШаблон:Переход, функцииШаблон:Переход (анонимные функцииШаблон:Переход, функции высшего порядка, рекурсивные определения функций, оптимизацию хвостовой рекурсии), модулиШаблон:Переход, приём и отправку сообщенийШаблон:Переход между процессами. ПрепроцессорШаблон:Переход поддерживает работу с макросами и включение заголовочных файлов.
Популярность Erlang начала расти в связи с расширением его области применения (телекоммуникационные системы) на высоконагруженные параллельные распределённые системы, обслуживающие миллионы пользователей WWW, такие как чаты, системы управления содержимым, веб-серверы и распределённые, требующие масштабирования базы данных. Erlang применяется в нескольких NoSQL-базах данных высокой доступностиШаблон:Sfn.
История
В середине 1980-х годов в компьютерной лаборатории компании Ericsson исследовали применимость существовавших на тот момент языков программирования для программного обеспечения телекоммуникационных систем. Джо Армстронг, Роберт Вирдинг (Robert Virding) и Майк Вильямс (Mike Williams) под руководством Бьярне Деккера (Bjarne Däcker), написав прототипы программ на различных языках, пришли к выводу, что ни один из этих языков не имел полного набора возможностей, необходимых в области телекоммуникационных систем. В результате был создан новый язык программирования — ErlangШаблон:Sfn. Своё название язык, вероятно, получил в честь датского математика и инженера Агнера Эрланга, основателя научного направления по изучению сетевого трафика в телекоммуникационных системах. По другой версии, название языка изначально было сокращением от «Ericsson language»Шаблон:Sfn.
Влияние на Erlang оказали ML, Миранда, Ада, Модула-2, CHILL, Пролог. Кроме того, на способ обновления программного обеспечения повлиял Smalltalk и использованные Ericsson проприетарные языки EriPascal и PLEXШаблон:Sfn.
Потребовалось четыре года развития языка и прототипирования с использованием виртуальной машины Пролога, после чего в 1991 году Майк Вильямс переписал виртуальную машину для Erlang на Си. В 1992 году Erlang был впервые использован в коммерческом проектеШаблон:Sfn. В 1995 году вышла новая версия Erlang, вобравшая накопившийся к тому моменту опыт использования языка. Язык был сочтён достаточно развитым для использования в других продуктах компании (решения для широкополосной связи, GPRS, ATM)Шаблон:Sfn.
В декабре 1995 года случилось событие, которое Джо Армстронг считает решающим для Erlang: проект AXE-N в Ellemtel по созданию нового маршрутизатора (как оборудования, так и системного программного обеспечения на C++) потерпел неудачу. В результате реорганизации проекта удалось, использовав разработанное оборудование и язык программирования Erlang, начать работы над ATM-маршрутизаторами серии AXD. Ресурсов лаборатории для такого проекта оказалось недостаточно, поэтому для работ по Erlang было создано производственное подразделение под названием OTP (Open Telecom Platform)Шаблон:Sfn. В 1996 году увидел свет одноимённый фреймворк OTPШаблон:Sfn.
НеожиданноШаблон:Sfn, в 1998 году топ-менеджмент Ericsson решил не брать на себя обязательств по разработке и поддержке собственного языка программирования, сосредоточившись вместо этого на Java. Использование Erlang было запрещено в новых проектах Ericsson Radio AB в связи с реализацией плана по аутсорсингу программной технологии компании Rational Inc.Шаблон:SfnШаблон:Sfn Это решение очень сильно повлияло на будущее Erlang: оно привело к открытию кода Erlang под открытой лицензией EPL (аналог Mozilla Public License)Шаблон:Sfn, а также послужило главной причиной начала распространения языка за пределами создавшей его компании. Основным возражением против открытия исходного кода являлось решение вопросов, касающихся патентов, но эти трудности были преодоленыШаблон:Sfn. Вскоре многие из основных разработчиков покинули Ericsson, чтобы организовать собственный бизнес — Bluetail ABШаблон:SfnШаблон:Sfn.
В начале 2000-х годов научные круги стали проявлять интерес к Erlang. С 2002 года стал проводиться ежегодный Erlang Workshop. Ericsson продолжал спонсирование проекта HiPE (от Шаблон:Lang-en — высокопроизводительный Erlang)Шаблон:Sfn уппсальского университета[Примечание 1]. Проект HiPE занимался эффективной реализацией языка и инструментами для проверки типов, а с 2001 года созданный в группе проекта компилятор в машинный код входит в поставку свободно распространяемой версии Erlang/OTPШаблон:Sfn. Работы, связанные с Erlang, ведут и другие высшие учебные заведения. Инструменты для рефакторинга созданы в Кентском университете в Великобритании и университете Ло́ранда Э́твёша в Венгрии, инструменты для различных видов тестирования — в Мадридском политехническом университете, техническом университете Чалмерса и Гётеборгском университетеШаблон:Sfn.
Когда системы с симметричной многопроцессорностью только начинали завоёвывать рынок серверов и настольных компьютеров, бросая вызов разработчикам программного обеспечения, уже в 2006 году первая версия Erlang с поддержкой SMP была выпущена совместными усилиями команды OTP из Ericsson и команды HiPEШаблон:Sfn. Вскоре после этого вышла первая почти за десятилетие крупная монография по Erlang: «Programming Erlang» Джо АрмстронгаШаблон:Sfn, после чего многие разработчики «открыли» для себя Erlang/OTPШаблон:Sfn, и язык стал набирать популярность[2].
Процесс развития языка включает в себя рассмотрение предложений по развитию — EEP (Шаблон:Lang-en). Через эти предложения Erlang-сообщество вносит изменения в стандартную поставку ErlangШаблон:Sfn. С внесёнными предложениями можно ознакомиться на веб-странице erlang.org/eeps[3].
Философия
По свидетельству Майка Вильямса, Erlang задумывался для решения трёх проблем разработки распределённых систем мягкого реального времени с высокой степенью параллелизма: возможности быстрой и эффективной разработки ПО; получения системы, устойчивой к программным и аппаратным сбоям, и возможности обновления системы «на лету», без простоя оборудованияШаблон:Sfn.
По словам Вильямса, философия, которой придерживались разработчики Erlang, подходит и для разработки программного обеспечения на этом языкеШаблон:Sfn: Шаблон:Начало цитаты
- Найдите наиболее подходящие методы. Проектируйте с использованием прототипов;
- Одних идей мало: нужно уметь их реализовать и знать, что они работают;
- Делайте ошибки в небольшом масштабе, а не в производственном проекте.
Большинство языков, созданных прежде Erlang, были разработаны без предварительного нахождения своей области применения, тогда как Erlang был разработан специально на основе требований к распределённым, отказоустойчивым, параллельным системам реального времени. С развитием сети Интернет оказалось, что многие приложения имеют аналогичные требованияШаблон:Sfn, чем и объясняется растущий интерес к языкуШаблон:Sfn.
Высокая отказоустойчивость кроется в применении изолированных друг от друга легковесных процессов, связанных лишь механизмом обмена сообщениями и сигналами выхода. Принцип разработчиков на Erlang по отношению к обработке ошибочных ситуаций в процессахШаблон:Переход можно выразить в виде высказывания: Шаблон:Начало цитатыПозвольте приложению упасть, и пускай что-то другое имеет с этим дело.Шаблон:Sfn Шаблон:OqШаблон:Конец цитаты или сокращённо — «let it crash» («пусть падает»). Связано это с тем, что в Erlang-системе легко следить за завершением процесса, завершать процессы, связанные со сбойным, и запускать новые процессыШаблон:Sfn.
Основные особенности
Высокоуровневые конструкции
Erlang является декларативным языком программирования, который скорее используется для описания того, что должно быть вычислено нежели как. Например, определение функцииШаблон:Переход, которое использует сопоставление с образцомШаблон:Переход для выбора одного из вариантов вычисления или извлечения элемента данных из составной структуры, напоминает уравнение. Сопоставление с образцом распространено даже на битовые строкиШаблон:Переход, что упрощает реализацию телекоммуникационных протоколовШаблон:Sfn.
Функции являются объектами первого класса в Erlang. В языке также широко применяются характерные для функциональной парадигмы программирования списковые включения (генераторы списков)Шаблон:Sfn.
Параллельные вычисления
Отличительной особенностью языка является применение легковесных процессов в соответствии с моделью акторов. Такой подход позволяет выполнять одновременно сотни тысяч и даже миллионы таких процессов, каждый из которых может иметь скромные требования по памятиШаблон:Sfn. Процессы изолированы друг от друга и не имеют общего состояния, но между ними можно установить связь и получать сообщения об их состоянии. Для взаимодействия процессов используется асинхронный обмен сообщениями. Каждый процесс имеет свою очередь сообщений, обработка которой использует сопоставление с образцом. Процесс, отправивший сообщение, не получает уведомления о доставке, даже если идентификатор процесса-получателя недействителен или получатель игнорирует сообщение. Таким образом, ответственность за правильно организованное взаимодействие между процессами лежит на разработчикеШаблон:Sfn.
Например, при реализации на Erlang сетевого чата структура программы может напрямую отражать одновременность действий пользователей по обмену сообщениями путём запуска новых процессов. Эффективность передачи сообщений сохраняется и при увеличении числа процессов, а требования к памяти минимизируются за счёт того, что легковесными процессами управляет виртуальная машина, а не средства нижележащей операционной системыШаблон:Sfn.
Распределённые вычисления
Erlang с самого начала проектировался для распределённых вычислений и масштабируемости. Распределение вычислений встроено в синтаксис и семантику языка, поэтому построение системы можно вести, абстрагируясь от конкретного места вычислений. В стандартной поставке Erlang может наладить связь процессов по протоколу TCP/IP независимо от поддерживаемых им нижележащих платформ (операционных систем)Шаблон:Sfn.
Работающий экземпляр среды выполнения Erlang (Шаблон:Lang-en) называется узлом (Шаблон:Lang-en). Программы, написанные на Erlang, способны работать на нескольких узлах. Узлами могут быть процессоры, многие ядра одного процессора, и даже целый кластер машин. Узел имеет имя и «знает» о существовании других узлов на данной машине или в сети. Создание и взаимодействие процессов разных узлов не отличается от организации взаимодействия процессов внутри узла. Для создания процесса на другом узле процессу достаточно знать его имя и, без особых на то оснований, он может не интересоваться физическим расположением взаимодействующего с ним процесса. Синтаксис отправки сообщения процессу на своём узле и удалённом — один и тот жеШаблон:Sfn.
Благодаря встроенным в язык возможностям распределённых вычислений объединение в кластер, балансировка нагрузки, добавление узлов и серверов, повышение надёжности вызывают лишь небольшие затраты на дополнительный код. По умолчанию узлы спроектированы для работы внутри обособленного сегмента сети (DMZ), но, если необходимо, коммуникация между узлами может происходить с применением защищённого криптографическими методами протокола SSLШаблон:Sfn.
Мягкое реальное время
Программы на высокоуровневом языке Erlang могут быть использованы в системах мягкого реального времени (которое иногда переводят как «псевдореальное» или «квазиреальное»Шаблон:Sfn). Автоматизированное управление памятью и сборка мусора действуют в рамках одного процесса, что даёт возможность создавать системы с миллисекундным временем отклика (даже несмотря на необходимость сборки мусора), не испытывающие ухудшения пропускной способности при высокой нагрузкеШаблон:Sfn.
Горячая замена кода
Для систем, которые не могут быть остановлены для обновления кода, Erlang предлагает горячую замену кода (Шаблон:Lang-en). При этом в приложении могут одновременно работать старая и новая версии кода. Таким способом программное обеспечение на Erlang может быть модернизировано без простоев, а выявленные ошибки исправленыШаблон:Sfn[4].
Описание языка
Типы данных
Типизация в Erlang является сильной и динамической. Динамическая типизация была выбрана для языка Erlang по причине того, что первые разработчики были больше с ней знакомыШаблон:Sfn. По мнению Джо Армстронга, статическая типизация потребовала бы очень больших трудозатрат, в частности, реализовать систему горячей дозагрузки кода было бы крайне затруднительноШаблон:Sfn. Такая типизация, при которой возможные ошибки типов выявляются только во время выполнения, тем не менее, не помешала создавать системы с очень высоким уровнем доступностиШаблон:Sfn. Данные в Erlang являются неизменяемыми: операции не переписывают старые значения, находящиеся в памяти. Если необходимо, модули на Erlang можно снабдить описаниями и определениями новых типов (не влияющими на компиляцию программы) для автоматической проверки типов с помощью утилиты DialyzerШаблон:Sfn.
Числа
В Erlang есть два типа числовых литералов: целые и с плавающей запятой, например: 125
, 4.5e-20
. Кроме обычной нотации, числа можно задавать через символ ASCII (например, $B
означает 66
) или вместе с указанием системы счисления с основанием от 2 до 36 (в старых версиях — до 16), например: 16#3f
, 2#1010
. В Erlang применяются целые числа произвольной точности и действительные числа двойной точности (64 бита), в стандарте IEEE 754—1985Шаблон:Sfn.
Для работы с числами можно использовать модуль math
, который содержит обычный набор математических функций и функцию math:pi/0
, возвращающую число <math>\pi</math>[5]. Пример вычислений в интерактивной оболочкеШаблон:Переход:
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) в других языках программирования (без необходимости предварительной декларации)Шаблон:Sfn. Атомы используются почти исключительно в сравнениях, имеющих в Erlang очень эффективную реализациюШаблон:Sfn. Кроме того, некоторые атомы имеют определённый смысл в возвращаемых значениях и описании исключений. К ним относятся: error
, ignore
, noreply
, ok
, reply
, stop
, undefined
Шаблон:Sfn.
Битовые строки и бинарные данные
Битовая строка используется для хранения в памяти нетипизированных данных. Строки, состоящие из целого количества октетов, называются бинарными (или двоичными) данными (Шаблон:Lang-en). Синтаксис описания битовой строки довольно гибок, так как описывает значения битов отдельных диапазонов и может быть снабжён модификатором типаШаблон:Sfn. Несколько примеров в интерактивной командной оболочке:
1> <<23,89,120>>.
<<23,89,120>>
2> <<"ABC">>.
<<65,66,67>>
3> <<10,17,42:16>>.
<<10,17,0,42>>
4> <<$a, $b, $c>>.
<<"abc">>
5> <<1024/utf8>>.
<<208,128>>
Выражения битовых строк (Шаблон:Lang-en) аналогичны списковым включениям, но работают над битовыми строкамиШаблон:Sfn:
1> << <<bnot(X):1>> || <<X:1>> <= <<2#111011:6>> >>.
<<4:6>>
В этом примере переменная X последовательно получает биты числа 2#111011
, которые затем инвертируются операцией битового отрицания bnot
(от Шаблон:Lang-en), в результате чего получается число 4.
Кортеж
Кортеж (Шаблон:Lang-en) — составной тип данных с фиксированным количеством элементов. При доступе к элементам кортежа с помощью встроенных функций нумерация элементов начинается с единицы, а не с нуля. Первый элемент кортежа принято использовать для указания роли кортежа в программе. Если первый элемент — атом, его называют тегом (Шаблон:Lang-en — «метка»). В Erlang принято строить различные типы данных на основе кортежей с тегами, что облегчает отладку программы и считается хорошим стилем программированияШаблон:Sfn.
Для работы с кортежами есть несколько встроенных функций, напримерШаблон:Sfn:
1> tuple_size({a, 1, "777"}).
3
2> element(1, {b, 2, 3, 4}).
b
3> setelement(1, {c, 5}, d).
{d,5}
Список
Список (Шаблон:Lang-en) — составной тип данных, содержащий переменное число элементов. Для манипуляции со списками можно применять функции модуля lists
стандартной библиотекиШаблон:Sfn. Формально список определяется как имеющий голову (Шаблон:Lang-en) и хвост (Шаблон:Lang-en), что выражается синтаксически в виде [HEAD|TAIL]
, где хвост обычно является списком (возможно, пустым). Пустой список обозначается []
Шаблон:Sfn.
Списки можно записывать и более привычным способом. Следующие записи эквивалентны:
1> [a|[b|[c|[]]]].
[a,b,c]
Для работы со списками можно применять списковые включенияШаблон:Sfn (генераторы списков), например:
1> [ X / 2 || X <- [1,2,3,4]].
[0.5,1.0,1.5,2.0]
Модуль lists
стандартной библиотекиШаблон:Переход содержит функции для обработки списков (и строк, так как в Erlang строка является списком)Шаблон:Sfn, такие как нахождение максимума, сортировка, изменение порядка элементов на противоположный, суммирование элементов и т. п. В следующем примере два списка склеиваются операцией конкатенации, а затем разбиваются на две части функцией 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
(Шаблон:Lang-en — свернуть, «r» от Шаблон:Lang-en — правая) для свёртки списка, первым параметром которой должна быть функция:
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
, а значение записывается в аккумулятор. По исчерпанию списка функция возвращает значение аккумулятораШаблон:Sfn. Функция является достаточно мощным средством, если учесть, что аккумулятор может быть списком или кортежемШаблон:Sfn.
Строка
В Erlang нет самостоятельного типа для строк: внутренне строки представляются списками. Синтаксически строку можно задать кавычками. Так, "Привет!"
равносилен (в подходящей кодировке) списку [1055,1088,1080,1074,1077,1090,33]
. Erlang поддерживает Unicode как в строке, так и в записи отдельного знака (через $
)Шаблон:Sfn.
Атомы и строки внешне достаточно похожи, но имеют совершенно различные реализации. Тогда как атомы можно только сравнивать, строки поддерживают многие другие операции, для них есть множество функций в модулях lists
и string
. Строка может выполнять функции атома, но память, занимаемая строкой пропорциональна её длине, тогда как атомы хранятся в системной таблице и на каждое использование атома в программе приходится лишь пара байтов, вне зависимости от длины атома. Сравнение двух атомов — это сравнение двух внутренних идентификаторов, выполняемое за одну операцию, тогда как сравнение строк предполагает поэлементный проход элементов строкШаблон:Sfn.
Логические значения
Для значений истина и ложь в Erlang применяются атомы true
(истина) и false
(ложь), которые и используются операциями сравнения, логическими операциями, встроенными функциямиШаблон:Sfn. Пример:
1> 2 < 3.
true
2> is_boolean(125).
false
Функциональный объект (Fun)
Fun-выражение позволяет создать анонимную функцию, например, для передачи в качестве параметра другим функциям. С помощью fun
можно также получить функциональный объект для функции из модуляШаблон:Sfn. Примеры:
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 был внесён синтаксис записей (Шаблон:Lang-en). Для работы с записями, необходимо в начале дать описание записи директивой -record
, например, для записи user
описание может быть следующимШаблон:Sfn:
-record(user, {login="anon", password, nick}).
Из этого описания компилятор узнаёт, что имеются в виду кортежи из четырёх элементов, в которых элементы со второго по четвёртый соответствуют полям login
, password
, nick
(порядок важен) записи с именем user
(определяется атомом в первом элементе кортежа). Значением по умолчанию для поля login
является строка "anon"
. Если значение по умолчанию не указано явно, подразумевается атом undefined
.
Создание записей и извлечение элементов записи всегда требует явного указания имени записиШаблон:Sfn:
R0 = #user{} % все поля получают значения по умолчанию
R1 = #user{login="user1", password="secret", nick="john"} % все поля получили значения
Синтаксис доступа к значениям полей записи: R1#user.login
, R0#user.nick
Шаблон:Sfn.
Ассоциативный массив
Ассоциативный массив (словарь) хранит пары вида «(ключ, значение)». В качестве как ключа, так и значения, может выступать любой терм Erlang.
Map = #{a => 2, b => 3, c=> 4, "a" => 1, "b" => 2, "hi" => 42},
Key = "hi",
maps:find(Key,Map).
{ok,42}
Другие типы
В языке Erlang имеются и другие типы данных. Тип ссылка (Шаблон:Lang-en) является практически уникальной в среде времени выполнения Erlang[6]. Ссылка создаётся вызовом функции make_ref/0
и может повториться через 282 вызовов этой функцииШаблон:Sfn. Ссылки можно сравнивать на равенство, а применяются они для одноразовых пометок или «волшебного печенья»Шаблон:Sfn.
Идентификатор порта (Шаблон:Lang-en) определяет порт для связи с внешним по отношению к Erlang-системе миром. Порт позволяет создавшему его процессу-владельцу (так называемому присоединённому процессу) обмениваться бинарными сообщениями со сторонними программами и ОС способом, принятым в данной операционной системеШаблон:Sfn[7]Шаблон:Sfn.
Идентификатор процесса (Шаблон:Lang-en), как и следует из его названия, идентифицирует процесс, порождаемый различными функциями spawn
. Идентификатор можно считать уникальным во время работы Erlang-системы, но в долго работающих системах могут всё-таки быть использованы повторно, что обычно не является проблемой на практикеШаблон:Sfn.
Встроенные функции для работы с типами
Для преобразования типов используются встроенные функции (BIF, от Шаблон:Lang-en) вида 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.0).
false
Операции
Арифметические операции
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
|
Все эти операции левоассоциативны. Унарные операции имеют наивысший приоритет, затем следует умножение и деление, наименьший приоритет у сложения и вычитания. При необходимости целое может приводиться к типу с плавающей запятойШаблон:Sfn.
Битовые операции
Битовые операции работают над целыми числами и дают в результате целое числоШаблон:Sfn.
Обозначение | Выполняемая операция | Пример | Результат примера |
---|---|---|---|
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
(ложь), получаемыми в результате сравнений и применения функций проверки типаШаблон:Sfn.
Обозначение | Выполняемая операция | Пример | Результат примера |
---|---|---|---|
not |
Отрицание (НЕ) | not true |
false
|
and |
Конъюнкция (И) | true and (1 < 5) |
true
|
andalso |
Аналогично and , но не вычисляет второй операнд, если первый false |
false andalso (1 < 5) |
false
|
or |
Дизъюнкция (ИЛИ) | is_atom("1") or is_atom(1) |
false
|
orelse |
Аналогично or , но не вычисляет второй операнд, если первый true |
true orelse (1 < 5) |
true
|
xor |
Исключающее ИЛИ | true xor true |
false
|
Операции сравнения
Операции сравнения получают два операнда, а результатом операции является логическое значение true
или false
. В Erlang есть следующие операции: ==
(равно), /=
(не равно), =<
(меньше или равно), <
(меньше), >
(больше), >=
(больше или равно), а также сравнения, которые работают без приведения к одному типу: =/=
(не равно в точности) и =:=
(равно в точности).
Можно сравнивать и значения разных типов, но они считаются в Erlang упорядоченными следующим образомШаблон:Sfn:
- число < атом < ссылка < функция < порт < идентификатор процесса < кортеж < список < бинарные данные
Списки считаются упорядоченными в лексикографическом порядке, а кортежи сравниваются по длине, и только затем в лексикографическом порядкеШаблон:Sfn.
Переменные
Переменные служат для хранения значений простых и составных типов. Имя переменной начинается с прописной буквы (в специальных случаях — с подчёркивания) и может содержать буквы, цифры, подчёркивания. Значение можно присвоить переменной лишь один раз — это свойство языка программирования называется единичным присваиванием (Шаблон:Lang-en)Шаблон:Sfn. К достоинствам единичного присваивания можно отнести устранение необходимости в блокировках, а также упрощение отладки программыШаблон:Sfn.
Передача параметров в функцию происходит по значению, поэтому все они вычисляются перед вызовом функцииШаблон:Sfn.
Область видимости переменной распространяется от момента её появления в заголовочной части описания функции или присваивания до конца части описания функции. ПримерШаблон:Sfn:
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
: её область видимости распространяется до конца этого клоза.
При выходе вычислений за пределы области видимости переменной память, занятая её содержимым, может быть освобождена в процессе сборки мусора, если значение переменной не используется в другой части программыШаблон:Sfn.
Сопоставление с образцом
Сопоставление с образцом используется в Erlang для присваивания (в том числе, при работе с параметрами функций), управления потоком выполнения программы, извлечения значений составных типов, выбора сообщения из очереди. В левой части сравнения (или в заголовке функции) могут находиться связанные (уже имеющие значение) и несвязанные (получающие значение) переменные, а также литералы (атомы, числа, строки). В результате исполнения сравнение может оказаться успешным (в этом случае переменные связываются со значениями) и неуспешным — переменные остаются несвязанными. В образце могут быть переменные, значение которых для образца безразлично: их имена записываются начинающимися с подчёркиванияШаблон:Sfn. Переменная с именем _
(подчёркивание) сопоставляется с любым значением, но при этом не происходит связывания. Такую переменную можно применять много раз.
Функции
Программы на Erlang состоят из функций, которые вызывают друг друга. Количество параметров функции называется арностью. При вызове функции заголовочные части описания функции сопоставляются с образцом. В случае совпадения параметров вызова, формальные параметры связываются с фактическими и исполняется соответствующая часть тела функцииШаблон:Sfn. Запись варианта вычисления функции для некоторого образца может называется клозом от Шаблон:Lang-en, а определение функции — это набор из одного или более клозов[8].
Для уточнения сопоставления с образцом в функциях можно использовать охранные выражения, которые следуют после ключевого слова when
Шаблон:Sfn. В примере ниже определена функция вычисления знака числа, которая рассчитывается в зависимости от сравнения параметра с нулём:
sign(X) when X > 0 -> 1;
sign(X) when X == 0 -> 0;
sign(X) when X < 0 -> -1.
Клозы Erlang перебирает в том порядке, в котором они записаны, пока не будет найден подходящий заголовокШаблон:Sfn. В охранных выражениях можно использовать только ограниченный набор встроенных функций, так как эти функции не должны иметь побочных эффектов.
Разумеется, функции Erlang поддерживают рекурсивные вызовы. В случае, когда определение функции оканчивается рекурсивным вызовом (хвостовая рекурсия), Erlang использует оптимизацию: стек вызовов не применяетсяШаблон:Sfn.
Как параметром, так и результатом функции может быть другая функция. В следующем примере функция одного аргумента возвращает функцию для прибавления аргументаШаблон:Sfn:
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).
Алгоритм сортировки, напоминающий быструю сортировку[4]:
-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
Это выражение всегда возвращает значение, соответствующее последнему вычисленному выражению в строке с подошедшим образцом. Это возвращаемое значение может служить возвращаемым значением функции, а может быть присвоено переменнойШаблон:Sfn. Как и в заголовочной части функции, после образца может следовать охранное выражение.
Упрощённым вариантом case-выражения является if-выражение:
if
охрана1 -> выражение11, выражение12, ...;
охрана2 -> выражение21, выражение22, ...;
...
охранаN -> выражениеN1, выражениеN2, ...
end
Здесь охранаi
— охранное выражение. Первое истинное охранное выражение вызывает выполнение соответствующих выражений, последнее из которых и является значением всего if-выражениеШаблон:Sfn. Следует заметить, что и здесь в охранном выражении можно применять только ограниченный набор операций и встроенных функций.
Запятые в охранном выражении работают как операция and
, напримерШаблон:Sfn:
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-выраженияШаблон:Sfn.
Препроцессор и макросы
Препроцессор Erlang (EPP) позволяет вкладывать файлы с исходным кодом один в другой, определять макросы и осуществлять простые и параметризованные макроподстановкиШаблон:Sfn. Макрос определяется с помощью директивы -define
, а макроподстановка осуществляется указанием имени макроса и возможных параметров после вопросительного знака (?
). Следующий пример показывает определение и применение параметризованного макроса:
-define(ZERO(X),X == 0)
is_zero(T) when ?ZERO(X) -> true;
is_zero(T) -> false.
Имя макроса обычно пишется прописными буквами. Определение макроса должно содержать лексемы Erlang целиком (например, попытка задать часть имени переменной с помощью макроса вызовет синтаксическую ошибку). Макросы могут использоваться для повышения удобочитаемости кода в охранных выражениях, для операторов отладки и т. п.Шаблон:Sfn Препроцессор имеет несколько предопределённых макросов, которые нельзя переопределить: ?MODULE
, ?MODULE_STRING
, ?FILE
, ?LINE
, ?MACHINE
[9].
Заголовочный файл (расширение .hrl
) с определениями макросов и записей можно включить при помощи директивы -include
Шаблон:Sfn.
Обработка ошибок
Для обработки исключительных ситуаций в Erlang можно применять конструкцию try-catch
, в общем случае записываемую в следующем видеШаблон:Sfn:
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
) для получения результата[Примечание 2]Шаблон:Sfn. После ключевого слова catch
следуют части обработки исключений, в которых в дополнение к образцам исключений могут быть указаны классы исключений (перед двоеточием): error
, throw
и exit
. Подчёркивание может использоваться как в образце, так и в классе исключенияШаблон:Sfn. Следующий простой пример иллюстрирует перехват ошибки класса 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
, которая принимает кортеж с более детальным описанием возникшей ошибкиШаблон:Sfn и генерирует исключение класса throw
. Использование этой функции нежелательно из-за ухудшения удобочитаемости кода программы, но может потребоваться в некоторых случаях при работе с вложенными структурами данных, например, при разборе XMLШаблон:Sfn. Исключения класса exit
возникают в результате вызова встроенной функции exit/1
или сигнала выходаШаблон:Sfn.
До разработки Ричардом Карлссоном (Richard Carlsson) из команды проекта HiPE описанного выше нового механизма обработки исключений (появился в версии R10B) в Erlang использовались catch-выраженияШаблон:Sfn.
Модули
Код программы на Erlang можно разбить на отдельные модули. Модуль — имя для набора функций, организованных в одном файле. Имя модуля должно совпадать с именем файла (если отбросить расширение)Шаблон:Sfn. Модуль можно откомпилировать в байт-код как из командной строки операционной системы, так и из командной оболочки ErlangШаблон:Sfn. В файле модуля можно записать объявления функций и директивы (иногда называются атрибутами)Шаблон:Sfn. Обязательным атрибутом является только -module(атом_имени_модуля).
Другой часто используемый атрибут — -export
— применяется для указания списка экспортируемых функций, то есть функций, которые можно использовать за пределами модуля.
Функции в Erlang однозначно определяются модулем, именем и арностью. Например, math:cos/1
соответствует функции cos
из модуля math
, принимающей один аргумент. Вызвать функцию можно так: math:cos(1.2)
Шаблон:Sfn.
Исходный текст модуля компилируется в BEAM-файл — файл, содержащий байт-код виртуальной машины BEAM (Шаблон:Lang-enШаблон:Sfn). В свою очередь, ERTS (Шаблон:Lang-en — система времени выполнения Erlang) выполняет этот кодШаблон:Sfn.
Процессы
Основной абстракцией параллельного программирования в Erlang является процесс. Процессы могут порождать другие процессы, выполняться одновременно, обмениваться сообщениями, реагировать на завершение друг друга.
Создание процессов
Для создания нового процесса служит несколько встроенных функций (spawn
и её аналоги)Шаблон:Sfn. Функции возвращают идентификатор процесса, который может использоваться, например, для отправки сообщений вновь созданному процессу. В интерактивной консоли erl можно получить список процессов и другую информацию посредством вызова функций processes().
и i().
соответственноШаблон:Sfn.
Отправка и приём сообщений
Как и язык Оккам, Erlang использует для отправки сообщения синтаксис с восклицательным знаком: ИдПроцесса ! Сообщение
. Приём сообщения — то есть извлечение его из очереди («почтового ящика») процесса — выполняется с помощью receive-выражений, обычно записываемых следующим образомШаблон:Sfn:
receive
образец1 when охрана1 -> выражение11, выражение12, ...;
образец2 when охрана2 -> выражение21, выражение22, ...;
...
образецN when охранаN -> выражениеN1, выражениеN2, ...;
НесвязаннаяПеременнаяДляОстальныхСообщений -> выражение1, выражение2, ...
end
Встретив такое выражение, интерпретатор последовательно просматривает сообщения из очереди. Каждое сообщение интерпретатор сопоставляет с образцом и, если оно удовлетворяет образцу, вычисляются соответствующие выражения. Когда все сообщения перебраны, и подходящего не оказалось, процесс блокируется в ожидании новых сообщений, после чего перебор очереди повторяется. Если в receive-выражении отсутствует образец, которому удовлетворяет любое сообщение, такое выражение называется выборочным receive-выражениемШаблон:Sfn.
Обработка ошибок и завершение процессов
Процесс можно связать с другим, в результате чего между процессами устанавливается двунаправленное соединение (Шаблон:Lang-en). В случае, если один из процессов завершается ненормально, всем связанным с ним процессам передаётся сигнал выхода (Шаблон:Lang-en). Процессы, получившие сигнал, завершаются, распространяя сигнал дальшеШаблон:SfnШаблон:Sfn. Сигнал выхода является кортежем, элементами которого являются атом 'EXIT'
(выход), идентификатор завершившегося процесса и причину завершения процесса. Причина завершения передаётся по цепочке завершающихся процессовШаблон:Sfn.
Процесс может осуществить перехват ошибки (Шаблон:Lang-en), если у него установлен флаг перехвата выхода[Примечание 3]. Такой процесс получает сигналы выхода связанных с ним процессов в виде обычных сообщений с той же структурой кортежа. Перехваченный сигнала выхода более не передаётся связанным с процессом-перехватчиком процессамШаблон:Sfn. Сигнал выхода с причиной — атомом normal
(нормальное завершение процесса) не вызывает завершения связанного процесса. Если же причина — атом kill
, процесс завершается безусловно (независимо от флага перехвата выхода), а связанным с ним процессам в качестве причины отправляется атом killed
, что даёт им возможность среагироватьШаблон:Sfn.
В Erlang есть возможность установить и однонаправленное соединение. При завершении наблюдаемого процесса процесс-наблюдатель получает сообщение с указанием причины завершенияШаблон:Sfn.
Процесс может остановить сам себя или другой процесс, вызвав функцию exit
Шаблон:Sfn.
Ввод-вывод
В планировщике процессов Erlang-системы проблема ввода-вывода, присущая многим другим языкам параллельного программирования, решена достаточно элегантно. Управление вводом-выводом, интегрированное с планировщиком, уже на самом нижнем уровне осуществляется на основе событий, что позволяет программе обрабатывать входящие и исходящие данные без излишних блокировок. Такой подход требует меньшего числа установки и разрыва соединений, а также убирает необходимость в блокировках и переключениях контекста. К сожалению, такой достаточно эффективный способ более сложен для понимания программистами, и находит применение в основном в системах с явными требования по высокой доступности и низкому времени отклика. Реализация событийно-ориентированного ввода-вывода встроена в Erlang-систему, что является ещё одним преимуществом при проектировании параллельных приложенийШаблон:Sfn.
Стандартная библиотека содержит модуль io
с функциями ввода-вывода. Такие функции содержат побочные эффекты, заключающиеся в появлении выведенной информации на консоли или записывании данных в файл на диске. Например, функция io:format
для форматированного вывода выводит строку с подстановкой параметров, возвращая в случае успеха атом ok
Шаблон:Sfn:
1> io:format("Пример вывода: ~p~n", [1]).
Пример вывода: 1
ok
Функции модуля io
включают в себя стандартный серверный интерфейс ввода-вывода. Протокол ввода-вывода Erlang (Шаблон:Lang-en) детально определяет связь клиента и сервера. Под сервером ввода-вывода понимается процесс, который обрабатывает запросы и выполняет запрошенные команды, например, на устройстве ввода-вывода. Клиентом является любой Erlang-процесс, которому требуется работать с устройством[10]Шаблон:Sfn.
Библиотеки
Стандартная библиотека модулей
Согласно официальной документации, стандартная библиотека модулей STDLIB[11] является обязательной для включения в минимальную систему Erlang/OTP[12] наряду с ядром Erlang. В библиотеку входят модули, предоставляющие разнообразные функции для работы со встроенными типами и другими структурами данных, ввода-вывода, обращения к среде окружения, для работы с файловой системой, процессами и т. п.
Модуль array
определяет (функциональный) абстрактный тип данных для динамического массива и имеет функции, позволяющие извлекать и обновлять элементы массива, определять рекурсивные функции для работы с массивами. Модуль string
расширяет возможности модуля lists
функциями для работы конкретно со списками символов, какими являются строки в Erlang. Модуль dict
(от Шаблон:Lang-en — словарь) содержит функции для ассоциативного массива, позволяющие хранить, извлекать и удалять значения по ключу, соединять массивы и производить итерации по элементам. Математические функции можно найти в модуле math
, а функции для генерации псевдослучайных чисел содержатся в модуле random
. Модуль calendar
предоставляет функции для григорианского календаря: запросы текущей даты, преобразования единиц измерения и интервалов времени, а модуль timer
содержит функции перевода интервалов времени к миллисекундам, запуска событий по таймеру и другие, связанные со временем, функции. Модуль erlang
содержит все встроенные функции Erlang, как общие, так и относящиеся к виртуальной машине. Модуль file
даёт доступ к функциям файловой системы, таким как открытие, чтение, запись, удаление файлов, а модуль filename
позволяет писать функции для манипуляции с именами и путями к файлам, абстрагируясь от конкретной операционной системы. Модуль io
предоставляет функции ввода-вывода. Кроме этих наиболее важных модулей, стандартная библиотека содержит и многие другие, с которыми можно познакомиться по документацииШаблон:Sfn.
Таблицы ETS и DETS
Для организации коллекций в оперативной памяти Erlang предлагает модуль ets (ETS, Шаблон:Lang-en — «хранилище термов Erlang»[Примечание 4]). ETS может хранить четыре вида коллекций: множество (Шаблон:Lang-en), упорядоченное множество (Шаблон:Lang-en), мультимножество (Шаблон:Lang-en), мультимножество с повторениями (Шаблон:Lang-en)Шаблон:Sfn. Доступ к элементам коллекций происходит по ключевому полю кортежа (ключи могут быть любых типов). Упорядоченные множества реализованы в виде бинарных сбалансированных АВЛ-деревьев, а остальные коллекции — с использованием хеш-таблицШаблон:Sfn.
DETS-таблицы дополняют функциональность ETS-таблиц (за исключением упорядоченных множеств), позволяя сохранять данные в файлахШаблон:Sfn.
Фреймворк OTP
OTP (Шаблон:Lang-en) является хорошо отлаженным набором полезных поведений (Шаблон:Lang-en) процессов и используется для создания серверных приложений. OTP формализует действия процессов и позволяет строить на их основе OTP-приложения (не следует путать с приложением — готовым программным продуктом). В модулях ОТР определены общие, стандартизированные шаблоны для конструирования параллельных приложенийШаблон:Sfn. Наиболее популярными поведениями являются обобщённый сервер и наблюдатель (Шаблон:Lang-en), но имеются и другие: конечный автомат, обработчик событийШаблон:Sfn. OTP содержит и другое связующее программное обеспечение (Шаблон:Lang-en), например, СУБД Mnesia.
OTP-поведения делятся на рабочие процессы (Шаблон:Lang-en), выполняющие собственно обработку запросов, и процессов-наблюдателей (Шаблон:Lang-en). В задачу последних входит слежение за рабочими процессами и другими процессами-наблюдателями — потомками. Деревья наблюдателей составляют OTP-приложение (Шаблон:Lang-en)Шаблон:Sfn. Документация по Erlang определяет OTP-приложение как компонент, реализующий некоторую функциональность, которая может быть независимо запущена на исполнение и остановлена как целое, а также повторно использована в других системах[13]. Разработчик приложения пишет код модулей функций обратного вызова (Шаблон:Lang-en), в которых и находится специфичная для данного приложения часть функциональностиШаблон:Sfn.
Хотя OTP строго говоря не является частью языка Erlang, он настолько вошёл в культуру и практику разработчиков на Erlang, что подчас между ними сложно провести границуШаблон:Sfn.
Разработка графического интерфейса пользователя
Разработка приложений с графическим интерфейсом пользователя (не считая веб-интерфейсов) может вестись при помощи библиотеки wxErlang — библиотеки wxWidgets, портированной для Erlang. WxErlang входит в стандартную поставку Erlang/OTP. WxWidgets написан на C++, поэтому перед разработчиками wxErlang стояла задача выразить средствами Erlang иерархию объектов. Несколько упрощая, в wxErlang классам соответствуют модули, а объектам — ссылки. Макросам на C++ соответствуют макросы Erlang. Некоторые типы данных, для которых в C++ были использованы классы, представляются в Erlang с помощью других типов данных, например, wxPoint задаётся в виде кортежа из двух элементов. События в wxErlang могут быть обработаны в Erlang либо с помощью функций обратного вызова (Шаблон:Lang-en), либо более естественной в среде Erlang передачей сообщенийШаблон:Sfn.
Программирование на Erlang
Интерактивная оболочка
Интерактивная оболочка (Шаблон:Lang-en) для Erlang может быть вызвана в Unix-подобных системах по команде erl
, в Windows — werl
Шаблон:Sfn. В оболочке можно вводить выражения и получать результат их выполнения, опробовать новый код, заниматься интерактивной отладкой, а также управлять системой, находящейся в промышленной эксплуатацииШаблон:Sfn.
В оболочке можно использовать дополнительные функции («команды»), доступные только в ней. Например, команда q().
осуществляет выход из оболочки с завершением всего, что делает Erlang-системаШаблон:Sfn.
В оболочке можно вызвать BREAK-меню с помощью Шаблон:Key (в Unix-подобных ОС) или Шаблон:Key (в Windows). В этом меню есть различные команды, в том числе Шаблон:Key — немедленный останов, Шаблон:Key — продолжение работы в оболочке и другие информационные и вспомогательные команды для работы с Erlang-системойШаблон:Sfn. Комбинацией клавиш Шаблон:Key вызывается ещё одно командное меню, с помощью которого можно, среди прочего, остановить «завесивший» оболочку процесс и вернуться в оболочку (Шаблон:Key и затем Шаблон:Key)Шаблон:Sfn.
Документирование и оформление кода
Текст от знака процента (%
) до конца строки считается комментарием в Erlang. Генерация документации из исходного кода в Erlang может производиться системой документирования EDoc. Для документирования кода модуля достаточно добавить определённым образом размеченный текст, а также файл overview.edoc
для документации уровня проекта (в последнем необязательно использовать знаки комментария)Шаблон:Sfn. Инструменты для работы с кодом на Erlang, например, erlang-режим в Emacs, подразумевают некоторые соглашения по употреблению символов комментария. Так, утроенный знак процента вызывает выравнивание по левому краю, удвоенный — выравнивание на уровне окружающего кода, а одиночный знак процента используется для обозначения комментария после кода, в конце строкиШаблон:Sfn. Разработчики Erlang выработали определённые стилевые соглашения, касающиеся организации и оформления исходного кода. Например, хорошим стилем считается понижение вложенности синтаксических структур, написание коротких модулей (менее 400 строк кода) и функций (не длиннее 15-20 строк кода), использование осмысленных имён для переменных и функций и т. п.[14]Шаблон:Sfn
Типы и анализ кода
Приложение Dialyzer, разработанное в рамках проекта HiPE и входящее в стандартную поставку, позволяет выявить ошибки (в том числе ошибки типизации) путём статического анализа кода. Программа TypEr, написанная Тобиасом Линдалом (Tobias Lindahl) и Костисом Сагонасом (Kostis Sagonas), является частью Dialyzer. Эта программа позволяет проверять определения типов функций, сверять указанный в директиве -spec
тип функции с её определением, выполнить вывод типовШаблон:Sfn. Программа TypEr выводит все типы, соответствующие успешному применению функции, в общем случае — лишь приблизительно, в более грубую сторону. Использование функции любым другим способом обязательно приведёт к ошибке времени исполненияШаблон:Sfn. В следующем примере показан синтаксис определения типа (директива -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 (от Шаблон:Lang-en — «анализатор противоречий для Erlang-программ») выявляет в коде отдельных модулей и целых приложений избыточные проверки, ошибки типов, недостижимый код. Все выявленные инструментом дефекты требуют устранения, так как инструмент не даёт ложных срабатываний. Для каждой функции всех проверяемых модулей Dialyzer устанавливает тип, используя основанный на ограничениях вывод типов и анализ потоков данных. После определения типов функций производится анализ противоречий в программеШаблон:Sfn.
Тестирование, профилирование, рефакторинг
Erlang предоставляет EUnit для модульного тестирования и фреймворк Common Test для системного тестирования. EUnit содержит средства для описания тестов, включая необходимый для этого набор макросов, а также производит вывод отчёта по окончании тестирования. Тестирование модулей происходит путём подключения заголовочного файла из EUnit, а функции с тестами могут быть как включены в сам тестируемый модуль, а так и вынесены в отдельныйШаблон:Sfn.
Тестирование параллельных программ можно выполнить с помощью Quviq Quick Check (версия Mini этого продукта доступна бесплатно)Шаблон:Sfn. Кроме тестирования, можно провести проверку всех возможных вариантов исходных данных с помощью метода проверки моделей. Для этого можно воспользоваться созданной в Мадридском политехническом университете отдельно распространяемой утилитой McErlangШаблон:Sfn.
Для профилирования кода и выявления степени покрытия кода тестами можно обратиться к модулям eprof
, fprof
, cover
и утилите cprofШаблон:Sfn.
Для Erlang разработаны несколько инструментов рефакторинга исходного кода, такие как RefactorErl, Wrangler, а также автоматическая, независимая от IDE утилита tidier. Утилита tidier позволяет автоматически находить и производить эквивалентные преобразование кода, например, заменяет
lists:filter(fun (X) -> is_something(X) end, L)
на
[X || X <- L, is_something(X)]
Шаблон:Sfn.
Эффективность
Как и многие другие языки программирования, Erlang имеет свои секреты написания эффективного кода. Совершенствование языка делает некоторые из трюков устаревшими, поэтому документация является лучшим руководством в вопросах оптимизации, в совокупности с профилированием и стресс-тестированием.
Например, при работе со списками не рекомендуется добавлять элемент в конец длинного списка с помощью конкатенации или функции добавления элемента к списку. Вместо этого сто́ит рассмотреть возможность добавления элемента в начало списка, а конечный результат обработать функцией обращения порядка элементов спискаШаблон:Sfn.
Свои рекомендации есть и для увеличения эффективности параллельных программ. Например, действия, требующие много памяти, лучше всего выделять в отдельный процесс, так как при этом затраты на сборку мусора будут минимальны: память будет освобождена по завершении процессаШаблон:Sfn.
Erlang и другие языки программирования
Подробное сравнение возможностей Erlang с другими языками можно найти в статье сравнение языков программирования.
Интеграция и гибридные языки
Erlang-система позволяет выполнять интеграцию с системами на других языках программирования. Имеются механизмы для сетевого взаимодействия с Си, Java, Лисп, Perl, Python, Ruby. Например, для более эффективного синхронного вызова небольших функций на Си можно использовать платформно-зависимые функции (Шаблон:Lang-en). Высокоуровневые библиотеки позволяют Erlang-системе представлять С или Java-узлы как обычные Erlang-узлы. Другие языки могут быть более тесно сопряжены со средой выполнения Erlang с помощью драйверов или сетевых сокетов посредством протоколов вроде HTTP, SNMP, IIOPШаблон:Sfn. Например, Ruby может взаимодействовать с Erlang посредством пакета erlectricity, а для Python разработана реализация Erlang-узла в виде пакета py-interfaceШаблон:Sfn.
Виртуальная машина Erlang находит применение и в других языках программирования, например, Elixir[15]Шаблон:Sfn и проекте Erl2 Джо Армстронга[16]Шаблон:Sfn. Кроме того, Роберт Вирдинг поддерживает проект Lisp Flavored Erlang («Erlang, приправленный Лиспом»), в котором синтаксис Лиспа используется с компилятором Erlang[17]. Есть и другие BEAM-языки: Efene, Joxa, Reia[18], Luerl, Erlog[19].
Официальный сайт упоминает[20] проект Erjang[21], в котором используется виртуальная машина Java.
Сравнение Erlang и C++ по производительности
Хотя опытные Erlang-программисты давно заметили, что их программы для тех же задач получаются более краткими по сравнению с другими широко используемыми в промышленности языками программирования, эмпирическое исследование показало, что для изученных телекоммуникационных приложений код на Erlang был на 70—85 % короче, чем на C++, а производительность системы при переписывании кода с C++ на Erlang возросла почти на 100 %Шаблон:SfnШаблон:Sfn. Для одного из использованных в исследовании проектов разница была объяснена написанием дополнительного C++-кода в рамках защитного программирования, управления памятью и кода для высокоуровневой коммуникации, то есть возможностями, которые являются частью языка Erlang и библиотек OTPШаблон:Sfn.
Сравнение взаимодействия процессов в Erlang и Go
Влияние теории взаимодействующих последовательных процессов Чарльза Э. Хоара чувствуется как в Go, так и в Erlang. В Erlang процессы, в соответствии с моделью акторов, отправляют сообщения друг другу напрямую. В Go то же самое происходит посредством каналов (Шаблон:Lang-en)[22]. Другим отличием является то, что каналы в Go имеют типы. В Erlang же нет типизации времени компиляции за исключением охранных выражений, что позволяет посылать процессам сообщения любого типа, но «непонятое» сообщение либо будет проигнорировано, либо навсегда останется в очереди[22]. Go позволяет легко организовать группу «go-программ» (Шаблон:Lang-en — намёк на Шаблон:Lang-en — сопрограмма) для получения сообщений из некоторого канала (такой подход известен как пул потоков). В Erlang, при проектировании которого уделялось особое внимание детерминизму и времени задержки (Шаблон:Lang-en), реализация рабочего пула возможна, но требует дополнительных усилий. Множественные отправители тривиально реализуются в обоих языках[22]. Erlang-процесс может послать сообщение и ждать на него ответ (соответствующий некоторому образцу), игнорируя другие сообщения в очереди. В Go такое невозможно, но подобная функциональность может быть достигнута созданием (в том числе, динамическим) новых вводов, то есть разделением каналов по назначению[22]. Go требует явного указания того, какие go-программы будут взаимодействовать с другими передачей сообщений, тогда как в Erlang отсутствует разделяемое между процессами изменяемое состояние (Шаблон:Lang-en) и поэтому изолированный процесс очень редко представляет интерес[22].
Абстракции взаимодействующих процессов достаточно похожи в Erlang и Go, однако во избежание ошибок при переходе с одного языка на другой следует учитывать нюансы: шаблоны, которые хороши в одном языке, могут не подходить для другого[22].
Критика
Как и любой язык программирования, Erlang не свободен от недостатков[23]. К погрешностям синтаксиса можно отнести зависимость от символа окончания выражения от контекста (это может быть .
, ,
или ;
), что требует дополнительного внимания при перемене выражений местами, излишнюю многословность записей (тип записи приходится упоминать при каждом доступе к члену записи), необходимость полного перечисления альтернатив в if-выражении во избежание выбрасывания исключения, если ни одно из условий не выполнено. К недостаткам можно отнести строго ограниченный набор функций, которые можно использовать в if-выражениях (этот недостаток можно обойти использованием case-выражений). Функциональный стиль и неизменяемые переменные приводят в некоторых приложениях (например, тесты) к бо́льшему количеству правок, чем в других языках программирования, так как вставка некоторой промежуточной обработки может потребовать новых имён переменных, что может привести к изменениям в коде, следующем далее по тексту. Из недостатков системы типов можно указать отсутствие строкового типа, а также невозможность динамически добавлять в записи новые члены. Есть проблемы и с организацией исходного кода, которая возможна только через создание нового файла, а также отсутствие пространств имён, классов или других средств для организации кода. Уровень качества модулей, за исключением основных, и документации оставляет желать лучшего[23].
Один из создателей языка, Джо Армстронг, в своём выступлении на конференции по истории языков программирования в 2007 году перечислил список областей, в которых Erlang можно было бы улучшитьШаблон:Sfn:
- Использование сборки мусора для атомов.
- Улучшение средств сопряжения со внешним кодом (Шаблон:Lang-en).
- Усиление изоляции между процессами.
- Более избирательная система безопасности среди узлов Erlang, основанная на различной степени доверия.
- Отдельные обозначения для протоколов и систем.
- Модули должны быть объектами первого класса.
Массовое распространение Erlang может сдерживать необычный для большинства программистов синтаксис, использование функциональной парадигмы, а также то, что наилучшая на 2010 год реализация языка использует виртуальную машину BEAM, а не более распространённую JVMШаблон:Sfn.
Сфера применения
В силу своих особенностей Erlang и существующих библиотек модулей Erlang подходит для создания сетевых серверов, распределённых систем, программ с GUI и подобных им интерактивных программ, инструментов для тестирования, управления и слежения, в общем, приложений с нерегулярным параллелизмом, в которых распараллеливаемые задачи достаточно разнообразны. Erlang не особенно хорош для написания кода, содержащего интенсивные вычисления с плавающей запятой, требующего включения нативного кода конкретной платформы или сильной оптимизации, а также для создания приложений, требующих синхронного параллельного выполнения задач. Не подходит Erlang и для проектов, в которых код должен исполняться на JVM или CLR, или проектов, требующих множества библиотек из других систем программированияШаблон:Sfn.
Можно сказать, что Erlang стал применяться для разработки облачных систем ещё до того, как сформировалось само понятие облачных вычислений[24]. Язык Erlang используется в масштабных телекоммуникационных и Интернет-приложениях многими компаниями, включая Amazon EC2 с реализацией SimpleDB, сервис социальных закладок Delicious, Facebook (бэкенд для чата), T-Mobile (сервис SMS и системы аутентификации)Шаблон:Sfn. Серверное программное обеспечение WhatsApp написано на Erlang. В январе 2012 года серверы WhatsApp под FreeBSD с 96 ГБ оперативной памяти смогли обрабатывать от 1 до 2,8 миллионов соединений[25][26].
Erlang часто ставят в заслугу легендарную надёжность ATM-коммутатора AXD301 (полтора миллиона строк кода на Erlang, полмиллиона — на C/C++) в сети British Telecom. По данным Ericsson, с момента установки в январе 2002 года за несколько лет случилась только одна незначительная неполадка, на основании чего надёжность системы по расчётам составила 99,9999999 %Шаблон:Sfn. Хотя более реальные оценки, учитывающие многие другие факторы, говорят всё-таки о «пяти девятках», успех маршрутизатора связывают с легкодоступными средствами разработки надёжных параллельных вычислений, встроенными в ErlangШаблон:Sfn.
Используется Erlang и в приложениях с открытым исходным кодом, среди которых CouchDB — документо-ориентированная база данных с REST-интерфейсом, Disco — фреймворк для распределённых вычислений на основе парадигмы MapReduce[24][27], Ejabberd — свободный (GNU GPL), распределённый и устойчивый к отказам Jabber-сервер, написанный в основном на Erlang, RabbitMQ — платформа, ориентированная на обработку сообщений (реализует AMQP, MQTT[28]), Wings 3D — программа 3D-моделирования и другие.Шаблон:Sfn
Для Erlang были написаны несколько веб-серверов: Yaws (Шаблон:Lang-en), Cowboy, а также MochiWeb — библиотека для создания HTTP-серверов[29]. Кроме того, были созданы несколько веб-фреймворков и систем управления содержимым, таких как N2O[30], Nitrogen, Chicago Boss, Zotonic, а также более не разрабатываемые активно BeepBeep, Erlang Web, ErlyWebШаблон:Sfn.
Среди другого известного программного обеспечения, выполненного на Erlang, можно выделить распределённую NoSQL базу данных Riak, спроектированную по принципам Amazon DynamoDBШаблон:Sfn, Flussonic (ранее известный как Erlyvideo) — видеостриминговый сервер, поддерживающий несколько протоколов[31]. Для стресс-тестирования распределённых систем можно применять (также распределённый) написанный на Erlang инструмент Tsung, который позволяет эмулировать тысячи (при достаточном количестве тестовых серверов — миллионы) одновременных пользователей[32].
Erlang практически идеально подходят для задач искусственного интеллекта (особенно вычислительного интеллекта, нейроэволюции), основанных на нейронных сетях. Подобное применение возможно благодаря имеющимся у Erlang пяти ключевым свойствам «языка программирования нейронных сетей»: изолированные процессы-нейроны (Шаблон:Lang-en), параллелизм (Шаблон:Lang-en, одновременность), механизм обнаружения сбоев, независимость от местоположения (Шаблон:Lang-en) и горячая замена кода. Примером такого применения является реализация одного из подходов к нейроэволюции — DXNNШаблон:Sfn.
Сообщество
Вокруг технологий 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Шаблон:Sfn.
Примечания
- Источники
Литература
- на русском языке
- на английском языке
- Шаблон:Книга
- Шаблон:Книга
- Шаблон:Книга
- Шаблон:Книга
- Шаблон:Книга
- Шаблон:Книга
- Шаблон:Книга
- Шаблон:Книга
- Шаблон:Книга
Статьи
- Шаблон:Статья
- Шаблон:Статья
- Шаблон:H
- Шаблон:Статья
- Шаблон:Статья
- Шаблон:Статья
- Шаблон:Статья
- Шаблон:Статья
- Шаблон:Статья
- Шаблон:H
Ссылки
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
- Шаблон:Cite web
Шаблон:Языки программирования Шаблон:Хорошая статья
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ 4,0 4,1 Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Книга
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Why I’m stopping work on Reia Шаблон:Wayback, 2011
- ↑ Federico Carrone, et al Spawned Shelter! Шаблон:Wayback
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ 22,0 22,1 22,2 22,3 22,4 22,5 Шаблон:Cite web
- ↑ 23,0 23,1 Шаблон:Cite web
- ↑ 24,0 24,1 Шаблон:Cite web
- ↑ Scaling to Millions of Simultaneous Connections Шаблон:Wayback, Rick Reed (WhatsApp) // Erlang Factory SF, March 30, 2012
- ↑ 1 million is so 2011 Шаблон:Wayback // WhatsApp Blog, January 6, 2012
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Cite web
- ↑ Шаблон:Книга
Ошибка цитирования Для существующих тегов <ref>
группы «Примечание» не найдено соответствующего тега <references group="Примечание"/>
- Страницы, использующие устаревший тег source
- Русская Википедия
- Страницы с неработающими файловыми ссылками
- Функциональные языки программирования
- Декларативные языки программирования
- Языки с динамической типизацией
- Свободные компиляторы и интерпретаторы
- Языки параллельного программирования
- Страницы, где используется шаблон "Навигационная таблица/Телепорт"
- Страницы с телепортом
- Википедия
- Статья из Википедии
- Статья из Русской Википедии
- Страницы с ошибками в примечаниях