erlang functional статьи

Знакомьтесь, Erlang

Dmitry Vasiliev 23:14, 2012 2 6 12:56, 2009 10 22

Впервые было опубликовано в журнале "Системный администратор" #8 за 2009 год

Язык программирования Erlang, первое знакомство.

В 1981 году шведская компания Ericsson запустила исследовательский проект, целью которого был поиск лучших способов программирования телекоммуникационных приложений. Основными требованиями была необходимость обеспечить быструю разработку высоконадежных приложений, работающих в течение долгого времени и поддерживающих параллельную обработку. В течение последующих 5 лет в рамках проекта было опробовано достаточно большое количество языков программирования, но не один не удовлетворял предъявляемым требованиям полностью. Было решено разрабатывать новый язык и в 1986-ом году была представлена первая версия языка программирования Erlang. До 1998-го года язык и сопутствующие библиотеки развивались внутри Ericsson, но в 1998-ом Erlang был выпущен как проект с открытым исходным кодом.

Основные особенности Erlang

Erlang — это язык программирования общего назначения и так же среда выполнения. Хотя Erlang как язык достаточно привлекателен сам по себе, его реальная мощь проявляется при соединении со средой выполнения (виртуальной машиной) и поставляемыми с языком библиотеками. Рассмотрим основные особенности Erlang:

  • Высокоуровневые конструкции языка. Erlang — это декларативный язык программирования, позволяющий описывать, что должно быть вычислено, вместо описания того, как это должно быть вычислено. Erlang так же использует динамическую типизацию, что может ускорить разработку приложений.
  • Параллельная обработка и передача сообщений. Вместо использования распространенной в настоящее время модели параллельного программирования, в которой используются потоки с разделяемой памятью, Erlang поддерживает параллельную модель, основанную на легковесных процессах с асинхронной передачей сообщений. Процессы в Erlang не имеют ничего общего с процессами операционной системы и называются процессами только потому, что код каждого процесса выполняется независимо от других процессов. При этом процессы Erlang за счет своей легковесности (время создания процесса составляет несколько микросекунд и не зависит от количества уже работающих процессов) работают даже эффективнее, чем потоки операционной системы, что позволяет работать с несколькими десятками тысяч процессов в одном приложении. Процессы общаются между собой посредством передачи сообщений (время передачи сообщения составляет несколько микросекунд так, как данные просто копируются из пространства одного процесса в пространство другого в рамках виртуальной машины), где сообщением может быть любое значение, используемое в языке. Сообщения передаются асинхронно, таким образом, сразу после посылки сообщения отправитель может продолжить работу. Входящие сообщения извлекаются из «почтового ящика» процесса выборочно и, таким образом, нет необходимости извлекать сообщения по очереди.
  • Распределенная обработка. Так как процессы в Erlang не используют разделяемой памяти, а общаются только посредством передачи сообщений, язык позволяет достаточно легко превратить параллельное приложение в распределенное, где различные процессы выполняются на различных узлах сети.
  • Надежность. Erlang поддерживает различные подходы для построения высоконадежных систем. На низком уровне процессы могут быть связаны между собой и оповещаться посредством сообщений при завершении связанного процесса. На более высоком уровне, при использовании OTP (см. врезку), появляется возможность устанавливать различные политики мониторинга отдельных процессов и групп процессов. В распределенной системе резервные узлы могут автоматически заменять узлы вышедшие из строя.
  • Работа в режиме мягкого реального времени (soft real-time). Хотя Erlang — это высокоуровневый язык, его можно использовать для систем работающих в режиме мягкого реального времени (когда нарушение временных ограничений приводит только к снижению качества работы системы). Язык использует автоматическое управление памятью, при котором «сборка мусора» происходит отдельно для каждого процесса в системе. Это позволяет получать ответы системы в пределах нескольких микросекунд даже при наличии «сборки мусора», что, в свою очередь, позволяет работать в режиме высокой нагрузки практически без потери пропускной способности.
  • Работа приложений в течение долгого времени. Язык поддерживает «горячую» замену кода модулей в работающем приложении, при котором в один момент времени могут работать старая и новая версии кода. Это необходимо для систем, которые просто не должны останавливаться для обновления кода, например, телефонных систем, или систем контроля трафика.

Проекты, в которых используется Erlang

Много коммерческих компаний используют Erlang в своих проектах, например:

  • Amazon использовал Erlang для создания SimpleDB, которая предоставляет сервис хранения данных для Amazon Elastic Compute Cloud (EC2).
  • Erlang использовался при создании Delicious, сервиса социальных закладок от Yahoo!, в котором зарегистрировано больше чем 5 миллионов пользователей и больше чем 150 миллионов закладок.
  • Facebook использовал Erlang для создания сервиса чата, обрабатывающего больше чем 100 миллионов активных пользователей.

Erlang так же используется и в проектах с открытым исходным кодом, например:

  • Ejabberd, сервис мгновенных сообщений, использующий XMPP протокол, написан на Erlang.
  • Erlang использовался для создания базы данных хранения документов CouchDB.

Начинаем работать

Перед началом изучения Erlang стоит убедиться, что он установлен в вашей операционной системе. Попробуйте набрать в командной строке команду erl:

$ erl
Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false]

Eshell V5.7.2  (abort with ^G)
1>

Для Windows вызов erl из командной строки работает только, если в переменную среды PATH добавлен путь к программе. В случае стандартной установки для Windows интерпретатор команд Erlang можно запустить через меню Start > All Programs > Erlang OTP.

Если Erlang установлен в вашей системе, то вы увидите приветствие похожее на пример выше. В случае, если при вызове erl вы получили сообщение о неизвестной команде, вам нужно установить Erlang. Для большинства систем Erlang может быть установлен с использованием стандартной системы управления пакетами. Для Windows инсталлятор может быть скачан с официального сайта: http://erlang.org/download.html. В других системах можно собрать Erlang с использованием исходного кода, который так же можно скачать с официального сайта: http://erlang.org/download.html.

Командная оболочка

Командная оболочка Erlang может использоваться для интерактивных сессий и выполнения выражений языка. Запустим оболочку командой erl (или через меню Windows, как описано выше) и наберем несколько команд:

$ erl
Erlang R13B01 (erts-5.7.2) [source] [rq:1] [async-threads:0] [kernel-poll:false]

Eshell V5.7.2  (abort with ^G)
1> % Это просто комментарий
1> 2 + 2.
4
2>

Рассмотрим нашу первую сессию подробнее:

  • При старте командой erl оболочка выводит заголовок с информацией о версии и опциях с которыми собран Erlang.
  • После этого выводится приглашение для ввода команд 1>. После первого приглашения мы набрали комментарий. Комментарии в Erlang начинаются со знака % и продолжаются до конца строки. Оболочка игнорирует комментарии.
  • Затем оболочка опять выводит 1> так как мы не набрали законченной команды. Теперь мы набираем 2 + 2, затем точку и нажимаем Ввод. Многие начинающие изучать Erlang забывают набрать точку в конце выражения, но в этом случае оболочка не сможет определить, что выражение закончено, и результат не будет выведен.
  • Теперь оболочка вычисляет выражение и выводит результат (4, в нашем случае).
  • После этого оболочка выводит приглашение в виде 2>. Где, 2 — это номер команды, который увеличивается с каждой выполненной командой.

Оболочка является достаточно мощным инструментом при работе с Erlang. Кроме ввода выражений поддерживается история команд (например, комбинации клавиш Ctrl+P и Ctrl+N используются для передвижения по введенным прежде командам), различные возможности редактирования команд и множество вспомогательных функций, которые можно посмотреть командой help(). Кроме этого, оболочка позволяет присоединяться к запущенным программам Erlang, даже на удаленных узлах (при этом так же имеется возможность автоматического использования защищенного соединения через SSH) и многое другое.

Надо заметить, что оболочка позволяет выполнять только выражения Erlang, но ее нельзя использовать для ввода других конструкций языка, которые мы будем рассматривать ниже.

Для выхода из оболочки можно использовать встроенную функцию halt():

1> halt().

В случае, если оболочка не отвечает (или как другой способ нормального выхода), то можно прервать выполнение нажатием клавиш Ctrl+C (или Ctrl+Break на Windows), после чего на экран будет выведено:

BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution

И теперь для прерывания выполнения можно нажать клавишу a.

Базовые понятия

После того как мы научились работать в оболочке Erlang, рассмотрим основные конструкций языка.

Числа

Как и во многих языках, арифметические выражения Erlang следуют обычным правилам для арифметических выражений, например 1 + 2 * 3 будет вычислено как 1 + (2 * 3).

Целые числа могут иметь произвольный размер и нет необходимости беспокоиться об арифметических переполнениях. Проверим это на практике:

1> 123456789 * 123456789 * 123456789 * 123456789.
232305722798259244150093798251441

Кроме этого целые можно вводить в различных системах счисления, используя следующую форму:

1> 10#10.
10
2> 16#10.
16
3> 2#10.
2

Здесь мы ввели число 10 в десятичной, шестнадцатеричной и двоичной системах.

Для ввода кодов символов можно использовать нотацию начинающуюся с символа $:

1> $a.
97
2> $%.
37
3> $0.
48
4> $\n.
10

Числа с плавающей точкой можно вводить в следующих форматах:

1> 1.25.
1.25
2> 2.0e-10.
2.0e-10

Для чисел с плавающей точкой Erlang использует 64-х битное представление, соответствующее стандарту IEEE 754-1985.

Переменные и сравнение с шаблоном

Имена переменных в Erlang должны начинаться с большой буквы, как в следующем примере:

1> X = 123456789.
123456789
2> X.
123456789
3> X * X * X * X.
232305722798259244150093798251441

В первой строке мы присваиваем значение числа 123456789 переменной X и затем значение выводится оболочкой. После этого мы выводим значение переменной и используем ее в арифметической операции.

Но при этом значение переменной Erlang может быть присвоено только один раз. Продолжая следующий пример:

4> X = 10.
** exception error: no match of right hand side value 10

Что здесь происходит? Строго говоря, оператор = в Erlang не является оператором присваивания, а является оператором сравнения с шаблоном. При этом, если переменной в текущей области видимости еще не присвоено значение, = действует как оператор присваивания, в противном случае = сравнивает значение с шаблоном.

При сравнении с шаблоном сначала вычисляется правая часть выражения, затем она сравнивается с шаблоном в левой части. В последнем примере переменная X является простейшей формой шаблона. В данном случае сравнение с шаблоном будет успешным только в случае, если значение правой части выражения равно 123456789:

5> X = 123456789.
123456789
6> X = 100000000 + 23456789.
123456789

Одноразовое присваивание избавляет разработчика от огромного класса ошибок связанных с неверным значением переменной, которая в противном случае могла бы быть изменена где-то между первым присваиванием и выражением, в котором она используется. Плюс к этому, такое поведение согласовывается с математической нотацией, где невозможна формула вида X = X + 1.

Атомы

В Erlang атомы используются для представления глобальных не-числовых констант. При сравнении с другими языками можно представить атомы, как элементы в перечисляемом типе данных. Значением атома является сам атом и единственная операция над атомами — это сравнение.

Атомы должны начинаться с маленькой буквы (как мы уже рассмотрели выше, переменные должны начинаться с большой буквы, так что они не пересекаются с атомами), за которой могут следовать буквы, цифры, @ и _ (точка тоже может использоваться в атомах, хотя это является не поддерживаемым разработчиками расширением языка). При использовании одинарных кавычек (') для атома могут использоваться практически любые символы. Примеры атомов:

1> hello.
hello
2> 'Hello, World!'.
'Hello, World!'
3> '1
3> 2
3> 3'.
'1\n2\n3'

Хотя Erlang не имеет отдельного булевского типа, по соглашению для этого используются атомы true и failse:

1> 1 > 2.
false
2> a < z.
true
3> is_boolean(1 > 2).
true
4> is_boolean(1 + 2).
false

Заметьте, что во второй строке мы сравнили два атома, при сравнении которых используется лексикографический порядок. Начиная с третьей строки, мы применили встроенную функцию (Built-in function, BIF) is_boolean, которая возвращает true, или false в зависимости от типа аргумента.

Кортежи

Кортеж — это составной тип данных, который используется для хранения связанных элементов в виде единого набора. Кортежи создаются с использованием фигурных скобок и их элементы разделяются запятыми. В качестве элементов кортежа могут выступать любые типы данных Erlang, например:

1> {1, 2}.
{1,2}
2> {1, 1.5, ok}.
{1,1.5,ok}
3> {ok, {5, 6}}.
{ok,{5,6}}

По соглашению, если первый элемент кортежа атом, его называют меткой, хотя это никак не отличает такой кортеж от других кортежей. С использованием таких меток можно создавать собственные типы данных, например:

1> {person, 'Joe', 'Armstrong'}.
{person,'Joe','Armstrong'}
2> {point, 10, 20}.
{point,10,20}

Такие метки упрощают извлечение информации из кортежа с использованием операции сравнения с шаблоном:

1> {point, X, Y} = {point, 10, 20}.
{point,10,20}
2> X.
10
3> Y.
20
4> {point, X, _, _} = {point, 10, 20, 30}.
{point,10,20,30}
5> X.
10
6> {person, Name} = {point, 20, 30}.
** exception error: no match of right hand side value {point,20,30}

Здесь {point, X, Y} — это более сложный шаблон, чем тот, что мы использовали выше, присваивающий переменным X и Y значения 10 и 20 соответственно. В строке 4 мы использовали анонимную переменную (_), которой не присваивается значение и ее можно несколько раз использовать в шаблоне. В строке 6, при попытке использования шаблона {person, Name}, мы получили ошибку, так как шаблон не совпадает с кортежем {point, 20, 30} в правой части выражения.

Кроме этого, с использованием встроенных функций есть возможность работать с отдельными элементами кортежей, например, получать элементы по индексу (начиная с 1):

1> element(2, {100, {1, 2, 3}, ok}).
{1,2,3}

Списки

Списки как и кортежи — это составной тип данных, который может содержать любые типы данных Erlang, но в отличие от кортежей списки обрабатываются совсем другим способом и служат для хранения данных, количество которых изменяется. Списки создаются с использованием квадратных скобок, и их элементы разделяются запятыми, например:

1> [1, 2.5, ok, {point, 20, 30}, [1, 2, 3]].
[1,2.5,ok,{point,20,30},[1,2,3]]

Первый элемент списка (в нашем примере — это 1) называется вершиной списка, а оставшиеся элементы — хвостом списка. При обработке списков наиболее эффективная операция — это работа с вершиной списка, которая поддерживается в Erlang следующей конструкцией: [вершина | хвост]. Здесь вершина — это элемент, или набор элементов для добавления, или удаления из списка, а хвост — список для добавления элементов, или список после удаления вершины. Рассмотрим это на примерах:

1> List = [3, 4, 5].
[3,4,5]
2> Range = [1, 2 | List].
[1,2,3,4,5]
3> [Head | Tail] = Range.
[1,2,3,4,5]
4> Head.
1
5> Tail.
[2,3,4,5]

Здесь мы создали список List, затем добавили в качестве его вершины два элемента, сформировав новый список Range. После этого мы отделили один элемент из вершины списка, сохранив его как Head.

Строки

Строго говоря, в Erlang нет отдельного типа данных для строк, строки реализованы просто как списки целых чисел. Для более удобной работы со строками их можно создавать с помощью двойных кавычек, как в этом примере:

1> "Hello, World!".
"Hello, World!"

Из следующих примеров можно увидеть, что строки и списки целых — это одно и тоже:

1> [$h, $e, $l, $l, $o] =:= "hello".
true
2> [$h, $e, $l, $l, $o].
"hello"

Здесь =:= сравнивает два элемента.

Бинарные данные

Для эффективного хранения бинарных данных рекомендуется использовать бинарный тип данных. Бинарный тип данных состоит из набора целых в диапазоне от 0 до 255 (или соответствующих символов) и представляется с помощью двух угловых скобок:

1> <<104,101,108,108,111>>.
<<"hello">>
2> <<"hello">>.
<<"hello">>

Заключение

В этой статье были кратко рассмотрены основные особенности и базовые понятия языка программирования Erlang. Более детальная информация по языку может быть найдена в документации, находящейся на официальном сайте и других сайтах, описывающих Erlang. Вот некоторые из полезных сайтов:

  • http://erlang.org/ - основной сайт языка, где всегда можно скачать последнюю версию дистрибутива, прочитать новости и документацию;
  • http://www.planeterlang.org/ - агрегатор блогов по Erlang;
  • http://trapexit.org/ - сборник статей, новостей и блогов по Erlang;
  • http://erlang.dmitriid.com/ - русскоязычный сайт с новостями и форумом по Erlang;

В следующей статье будет рассмотрено последовательное программирование в Erlang