Wednesday, July 02, 2008

Erlang job 2

И, чтоб два раза не вставать: тут обнаружилась еще одна интересная вакансия в России (Москва) на Эрланге, в стартап http://artytalk.com/:

Server-side developer:
— Писать вместе с командой application server под нагрузку в миллионы пользователей (Erlang, Amazon Elastic Cloud)
— Иметь серьезный опыт разработки высоконагрузочных масштабируемых приложений
— И снова — любить наш проект, быть хорошим и веселым человеком с мотивацией сделать мир лучшим местом

Подробнее здесь


Io

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

Язык крайне приятный, очень объектно-ориентированный и минималистичный, любителям smalltalk, javascript, ruby или lisp должно понравиться.

Кроме того, у нас есть целый irc канал #io-ru на freenode, так что если ио заинтересовал — присоединяйтесь, да и просто заходите, там уже тусуется некоторое количество знатных рубихакеров и хаскелеводов.


Wednesday, May 21, 2008

Map/Reduce своими руками — Apache CouchDb

 

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

Нет, я вполне понимаю когда у вас действительно приложение ориентировано на обработку и хранение больших массивов данных. Ну, ERP-системы, всякие хранилища, статистика там, "в прошлом месяце продали сто тыщ карандашей, в этом двести".

С другой стороны, в большинстве случаев, когда речь идет о десктопных (или веб-) приложениях, где не нужно ворочать миллионами примитивных записей,  а приложение работает с относительно высокоуровневыми, сложными объектами, суть "дизайна и проектирования баз данных" заключается в повторении двух действий:

а) расколупать эти высокоуровневые объекты на кучу простейших полей — чисел, строк и сложных зависимостей между ними, и раскидать  между десятками таблиц. Обычно это не очень сложно, однако некоторые (или многие?) типы данных не так уж приятно и органично раскладываются в этой модели — например, теги у записей в блоге;

б) затем упорно собирать эти поля в объекты обратно, пользуясь четырехэтажными JOIN'ами, мегабайтами кода врапперов, кривыми и не очень слоями ORM — в зависимости от квалификации разработчика, в общем, всячески преодолевать пресловутый O/R impedance mismatch. При этом и рукописные JOIN'ы не показывают чудеса производительности и гибкости, а сгенеренные автоматически умным слоем врапперов — тем более.

SalesLogix v7 Database SchemaВ принципе, ORM-библиотеки в динамических языках (см. SQLAlchemy) довольно приятны в обращении, однако и они не позволяют элегантно решить еще один болезненный вопрос — с апгрейдом схемы.

 

В общем, немало приложений используют базы данных для хранения сложных структур данных, и при этом действительно сложные запросы с использованием внутренних зависимостей к этим данным им на практике нужны редко, или вообще не нужны (если не считать мега-JOIN'ов для того, чтобы просто обратно выковырять эти свои структуры из БД). Похоже, обычная RDBMS мало подходит для них — упомянутые выше проблемы довольно болезненно решаются, а миллионы человекочасов тратятся разработчиками БД на реализацию других, бесполезных для них возможностей.

Одним решением являются объектно-ориентированные хранилища, они действительно становятся довольно популярны и заслуживают отдельного разговора. Они прозрачно решают проблему с ORM, но, если говорить о веб-приложениях (которые весьма интересуют нас в свете обещаной новой версии defun.ru :), объектно-ориентированные базы это таки не совсем то, что доктор прописал — они не решают проблем горизонтальной масштабируемости и распределенности данных, да и веб — это прежде всего много текстовой информации, неплохо было бы как-то учесть и это.

Логотип CouchDb

Итак, CouchDb — документоориентированная база данных. Хранить она умеет документы — объекты, состоящие из кучи полей с произвольной структурой. У каждого документа есть только два обязательных служебных поля: имя и версия, имена уникальны и находятся в линейном пространстве — представьте себе гигантскую директорию с файлами-документами, вроде такого:

{
 "_id":"63086444D554D3094C080F96D5005B03",
 "_rev":"1837603925",
 "author":"lrrr",
 "tags":["baz","test","ru"],
 "url":"http:\/\/incubator.apache.org/couchdb",
 "title":"couchdb home",
 "description":"boo boo ba ba",
 "type":"story",
 "comments":1,
 "votes":2
}

Версии нужны для организации параллельного доступа к базе данных — вспомните как работает ваша система контроля версий — если мы хотим изменить документ, мы просто берем его, меняем и пытаемся положить обратно — если за это время его версия не поменялась, все отлично, если поменялась — можно просто попробовать внести те же изменения еще раз, с новым документом, или еще как-то сделать merge (в зависимости от приложения). Это называется optimistic locking, основной плюс — никто не блокирует документ на время редактирования, и поэтому не нужно ждать разблокировки. Кстати, такой механизм может применяться и в некоторых современных RDBMS, только на уровне строк в таблице (см. http://www.google.com/search?q=%22row+versioning%22).

Архитектура CouchDb Интерфейс к CouchDb — исключительно HTTP, исключительно  REST, а ответ от сервера приходит в формате JSON. Поначалу это несколько настораживает — не самый эффективный протокол, но с учетом того что высокоуровневые документы хранятся в ней целиком, делать по 5-10 запросов к базе на каждый чих и не нужно. А плюсов куча: во-первых, любой язык умеет работать с HTTP и JSON (а если не умеет, легко научить), во-вторых — легко отлаживать, в третьих — CouchDb понимает HTTP Etag и If-None-Match, а значит можно без особых усилий прикрутить к базе HTTP кэш. 

Зато масштабироваться вширь все должно отлично — в конце концов, примерно по такой схеме построены и Amazon SimpleDb, и Google BigTable. Удивительное, кстати совпадение, но и SimpleDb, и CouchDb написаны на эрланге ;)

 

 map & reduce

Что выгодно отличает CouchDb от сервисов гугла и амазона, так это более "продвинутая" функциональность в области запросов к данным.

Естественно, что менее структурированные данные обрабатывать сложнее, и, раз уж мы так заботимся о масштабируемости — запросы эти тоже должны легко распределяться по кластеру серверов БД. Для этого CouchDb использует паттерн map/reduce, описаный в известной статье инженеров Google.

На практике выглядит это так: на сервере, в специальных документах хранятся view-функции (собственно map() и reduce()), преобразующие набор документов нужным образом, и к ним можно обращаться с помощью того же REST интерфейса. Вычисляться они умеют постепенно, с сохранением промежуточных результатов, то есть, если между двумя вызовами view добавился или изменился один два документа, то функция будет вызвана только для них. Пишутся они на JavaScript, но можно несложно подключить вместо этого python/ruby/что-то еще.

В качестве дополнительного бонуса — поддержка полнотекстового поиска по документам, с помощью любой внешней библиотеки (пока авторы прикрутили к CouchDb поисковик Apache Lucene).

* * *

В конце обычно принято немного попинать рассматриваемую технологию, но CouchDb мне пинать пока жалко — слишком приятное впечатление производит. Хотя, конечно, это пока всего лишь альфа-версия, со всеми вытекающими последствиями (reduce, скажем, появился в транке три дня назад). Да, она очень небыстрая — пока умеет обрабатывать порядка десятков insert'ов в секунду (если не использовать режим bulk update) и да, она жрет очень много дискового пространства — так как все промежуточные версии документа сохраняются, если их периодически не удалить специальной функцией "Compact Database" — впрочем, это можно делать параллельно, не останавливая приложение. Однако для альфы система весьма стабильна и уже имеет, среди всего прочего, очень приятный и функциональный веб-интерфейс для администрирования и разработки.

 

Ссылки:

 

27723148.0f886b0078628df55b2305efb1cb3729.1211322056.6554977ddd1d9ebd9f3e14154e6e8540


Friday, April 11, 2008

Анонс

Тем временем мы с Сёмкой организовали тут недодигг для программистов, утилизировав под это его давно пустовавший домен defun.ru

header_logo_t

Основная тема — новости и ссылки о функциональном программировании, разных альтернативных и перспективных языках, ну и про computer science вообще. Ключевые слова — хаскель, erlang, lisp, smalltalk, ocaml. Ruby, groovy и питон туда тоже отлично впишутся.

Сервис находится в состоянии лямбда-тестирования ©, так что некоторое время все еще будет несколько глючить и тормозить, так что баг репортам мы будем рады -> пишите на lrrr@defun.ru или semka@defun.ru.

Надеемся, что когда-нибудь в светлом будущем это станет аналогом dzone или вроде того ;)


Monday, March 03, 2008

Мои пять инструментов

Про эрланг серия немного прервалась в связи с отпуском, но продложение обязательно будет — про более прикладную часть, и веб-приложения тоже. Оставайтесь на линии.

А пока присоединяюсь к флешмобу про пять инструментов —правда, не буду здесь особенно оригинален.

  1. Far + Colorer — лучшая среда обитания
  2. MS Visual Studio + Visual Assist — лучшая IDE для C++
  3. python для всего, мелкого и среднеразмерного, что надо сделать быстро (иногда еще люблю в этом качестве sh и ghci)
  4. subversion — ну куда ж без контроля версий. Давно порываюсь попробовать bazaar/darcs/mercurial, но все как-то недосуг..
  5. Для коммуникации — gmail — исключительно в качестве почтового сервиса и клиента, и gajim — в качестве IM

Последние продуктивность скорее снижают, но и без них я обойтись не могу никак :)

А вот любимого редактора для петона/erlang/haskell у меня пока нету — во-первых, стараюсь как раз не особенно привыкать к конкретному редактору, чтоб поменьше таскать с собой настроек с одной системы на другую, а во-вторых, я нахожусь в состоянии медленного переползания на линукс с винды, и окончательного выбора emacs/vim/netbeans/... пока не сделал (хотя vim пока лидирует).


Saturday, February 09, 2008

Yaws — веб-сервер на Erlang

В самом начале обещал рассказать про веб-приложения на эрланге. Наверное, пора сворачивать ближе к теме.

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

Сначала насчет производительности:

Картинка известная, подробнее узнать, как она получена можно тут.  По горизонтали это количество параллельных запросов  к серверу (очень медленных клиентов), по вертикали — сколько кб/с при этом параллельно может отдавать сервер одному быстрому клиенту. Красненькое это yaws, синенькое и зелененькое это Apache, который после нескольких тысяч тихо умирает.

Конечно, Apache сравнивать с yaws не очень честно — все-таки апач продукт сложный и намного более многофункциональный, с кучей дополнительных модулей и настроек, и, в общем-то использовать его для отдачи статических страниц не очень принято. Однако ж картинка все-таки очень показательна, возможность держать много параллельных соединений важна в любом случае, да и с динамикой дела у yaws должны быть тоже хорошо (см. например Erlang vs PHP на Computer Language Shootout).

Пару слов об установке Yaws — с линуксом проблем, конечно, нет, на винду поставить тоже можно из-под Суgwin, но чтобы избежать лишней головной боли, обратите внимание, чтобы в пути к эрлангу и директории home не было пробелов (а последняя по умолчанию в "Documents And Settings" находится). Еще пара полезных замечаний про процесс установки есть тут.

 

Настраивается yaws с помощью конфиг-файла, там все довольно тривиально — дефолтный файл можно пока особенно не менять, разве что установить root директорию (параметр docroot).

Простейшее приложение с использованием yaws:

<head><title>Hello world using Yaws</title></head>
<body>
<erl>
out(A) ->
   case queryvar(A, "name") of
      {ok, Name} ->
        {html, "<div style=\"font-size: 12pt;\"> Hello, " ++ Name ++"</div>"};

      undefined ->
        {html, "<b> Name parameter required </b>"}
    end.
</erl>
</body>

 

Сохраняем его как hello.yaws в root директории.

hello-yaws

.yaws файл, как уже видно, это просто HTML шаблон. Каждый блок между тегами <erl> и </erl> компилируется в отдельный Erlang модуль. Каждый такой блок должен содержать функцию out/1, которой в качестве параметра передается структура, содержащая параметры запроса. Есть много вариантов как можно возвращать ответ, наиболее интересны два:

использованный выше

{html, HtmlString}

Где HtmlString — просто кусок HTML в виде строки, как в примере выше.

И чуть более удобный

{ehtml, Term}

Где Term — тот же HTML в виде терма Erlang, где тэги представляются кортежами или списками кортежей. Например, вот такой терм

{p, [],
    [
        {b, [], "Hello World"},
        {a, [{href, "http://www.example.com"}], 
           "link"}
    ]
}

преобразуется сервером в HTML-строку

<p>
  <b>Hello World</b>
  <a href="http://example.com">link</a>
</p>

Выглядит немного жутковато, но быстро привыкаешь и работать с кортежами становится намного удобнее чем со строками.

Еще Yaws, кроме того что является просто веб-сервером, предоставляет немало дополнительных инструментов, таких как автоматическая поддержка сессий, cookie, механизмов удаленного вызова процедур на основе SOAP и JSON-RPC.

Для полноценного фреймворка — аналога Ruby on Rails и прочих, API Yaws, конечно, не дотягивает (для этого есть библиотека ErlyWeb), но для небольших приложений этого должно быть вполне достаточно.


Wednesday, February 06, 2008

Erlang: инструменты

Наверное, в самом начале неплохо было б написать про средства отладки, установке Erlang и так далее — так вот, исправляюсь.

Вообще, некоторая проблема с Эрлангом состоит в том, что его разработчики очень не любят писать статьи для начинающих, рисовать туториалы с красивыми скриншотами. Документации по некоторым модулям вообще минимум, по другим — в основном user reference, иногда маленький юзер гайд. Поэтому сначала кажется что разработчик вынужден жить исключительно  наедине с консолью интерпретатора — однако это не так!

IDE

В качестве IDE можно использовать ErlyBird — IDE для Эрланга на платформе NetBeans, соответственно получаем от нее все соответствующие плюшки, выглядит очень красиво:

Screenshot-ErlyBird 070930

Есть еще проект ErlIDE на базе Eclipse, но про него ничего сказать не могу — не пробовал.

Ортодоксальные товарищи также, конечно, могут заюзать vim, emacs или Far под виндой (простенькими hrc файлами для colorer могу поделиться).

Интерпретатор

erl это собственно интерпретатор Эрланга. Я, кстати, тут везде подразумеваю, что у вас установлен последний релиз R12 — там действительно есть порядочное количество  новых полезных плюшек.

Полезные функции интерпретатора:

- скомпилировать и загрузить модуль

> c(boo.erl).
  {ok}

 

- установить текущую директорию

> file:set_cwd(Dir).

 

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

Можно это сделать с помощью того же интерпретатора:

> erl -noshell -run <module> <function> [Params]

(Модуль предварительно надо скомпилировать в байт-код, из интерпретатора или с помощью компилятора erlc).

После окончания работы функции интерпретатор останется висеть, чтобы он закрывался автоматически, можно добавить флаг "- s init stop" (тут за подсказку спасибо анонимному комментатору  предыдущего поста).

Либо, для большего удобства, предусмотрен вариант запуска программы как скрипта — с помощью утилиты escript. Ей в качестве единственного аргумента передается имя скрипта, а сам он должен экспортировать функцию main(ArgList):

-module(test).
-export([main/1]).

main(Args) ->
    io:format("Hello world~n").
> escript test.erl
test.erl:4: Warning: variable 'Args' is unused
Hello world

 

Отладчик

Кроме этого есть вполне функциональный отладчик с GUI. Если мы хотим поотлаживать наш модуль foo:
- компилируем его и загружаем его с отладочной информацией

1> c(foo, [debug_info]). 
{ok, foo}

 

- Вызываем монитор эрланговских процессов

2> im(). 
<0.181.0>

Screenshot-Monitor

- загружаем модуль в отладчик

3> ii(foo). 
{module, foo}

 

- ставим брейкпоинт на загрузку модуля

4> iaa([init]). 
true

Screenshot-Attach Process _0.31.0_

Отладчик, естественно, умеет работать по сети, отслеживать сообщения, приходящие процессам, и много всего другого.

Остальное

Если приложение использует эрланговскую распределенную БД mnesia, то нередко нужен удобный способ заглянуть что лежит в табличках. Для этого есть table viewer, который загрузить можно функцией

> tv:start().

Screenshot-tv2

Dialyzer — утилита для статического анализа модулей. Иногда позволяет найти интересные ошибки без запуска программы. Запускается так:

> dialyzer:gui(). 
Хотя с некоторых пор dialyzer идет как отдельное приложение в пакете с Erlang.

Screenshot-Dialyzer v1.7.0 @ lrrr-desktop

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


Saturday, February 02, 2008

Erlang: распределенные приложения

Основная фишка erlang — это все-таки его жуткая распределенность.

Модель программирования основана исключительно на посылке сообщений. То есть никакой расшаренной памяти, никаких,
мьютексов, евентов и прочей ерунды, обычно ассоциирующейся с параллельными (concurrent) приложениями.

Примитивных операций есть три:
- можно создать процесс с помощью функции spawn:

spawn(Function)

- можно ему послать сообщение с помощью выражения !

Pid ! Message

- и получить сообщение

receive
    Pattern1 -> ...
    Pattern2 -> ...
    Pattern3 -> ...
after N ->
    ...
end

 

    Конструкция receive очень похожа на выражение case, только для обработки таймаутов можно добавить кейс after N,
где N — время в мс, по истечении которого перестаем ждать ответа.

[Избитый] простейший пример, с процессами, пингующими друг друга:

-module(ping_test).
-compile(export_all).

ping() ->
% создаем процесс pong
    Pid = spawn(fun pong/0),
% посылаем ему сообщение -- атом ping и свой Pid
    Pid ! {ping, self()},
% ждем от него сообщений
    ping_loop(Pid).

% обработка сообщений от процесса pong
ping_loop(Pid) ->
    receive
        {pong, Pid} ->
% получили сообщение pong и Pid отправителя
            io:format("received pong~n"),
% ждем случайный интервал времени от 0 до 1200 мс.
            timer:sleep(random:uniform(1200)),
% шлем ему опять сообщение ping
            Pid ! {ping, self()},
            ping_loop(Pid) % и опять ждем...
        after 1000 ->
            io:format("pong timed out~n")
    end.

% входная точка процесса pong
pong() ->
    receive
        {ping, Pid} ->
% получили сообщение ping и Pid отправителя
            io:format("received ping~n"),
% подождем..
            timer:sleep(random:uniform(1200)),
% шлем обратно pong
            Pid ! {pong, self()},
            pong() % цикл
    after 1000 ->
        io:format("ping timed out~n")
    end.


 

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

1> c(ping_test).
{ok,ping_test}
2> ping_test:ping().
received ping
received pong
received ping
received pong
received ping
received pong
received ping
connection to pong timed out
ok
3> connection to ping timed out

 

Рантайм обеспечивает, что сообщения доставляются в том же порядке, как и были отправлены, и никогда не выпадают из последовательности (то есть, если процесс N+1 сообщение не будет доставлено, если пропало N-ное).

Процессы можно легко и непринужденно запускать и на удаленных машинах, с помощью разновидности той же функции spawn. В большинстве случаев никакой разницы между локальными и удаленными процессами нет.

Чтобы превратить пример с ping в распределенное приложение, нужно просто заменить вызов

spawn(fun ...)

на

spawn(node, fun ...)

Где node это атом-имя узла на котором требуется запустить процесс. виртуальной машины. (А узел (node) — это просто запущенный экземпляр виртуальной машины Erlang).

Итак, немножко изменим функцию ping (модифицированный исходник тут):

ping(Node) ->
    Pid = spawn(Node, fun pong/0),
    Pid ! {ping, self()},
 ping_loop(Pid).

Теперь запустим два интерпретатора в качестве узлов с именами boo и foo (оба должны иметь доступ к скомпилированному модулю, так что проще запустить их из одной директории):

$ erl -name foo@127.0.0.1
$ erl -name boo@127.0.0.1

(boo@127.0.0.1)2> c(ping_distrib). {ok,ping_distrib} (boo@127.0.0.1)1> ping_distrib:ping('foo@127.0.0.1'). received ping received pong received ping received pong received ping received pong received ping connection to pong timed out ok (boo@127.0.0.1)2> connection to ping timed out

Создание процесса в Эрланге — очень дешевая операция (цифры — порядка сотен тысяч созданных и убитых процессов в секунду, см. бенчмарк здесь ).

Запущенные процессы также жрут очень мало памяти, а в режиме ожидания сообщений — еще и не жрут CPU (например, 500000 таких спящих процессов у меня ест 0% CPU и 600 Мб памяти — около 1.4 кб на процесс).


Механизмы обработки ошибок в Erlang, конечно, тоже распределенные.

В случае ошибки процесс умирает, и система рассылает всем процессам, связанным с ним специальное сообщение exit. Если тот процесс эти сообщения не отлавливает (для чего у процесса должен быть установлен специальный флаг trap_exit), то система убивает и его, и так далее по цепочке.

erlang-process-crash

Связываются процессы с помощью функции link/2. Для удобства есть еще spawn_link/2, и если ее использовать в нашем примере вместо spawn, а также выходить из процессов не нормально, а с помощью erlang:error/1 (см. модифицированный исходник тут), вывод будет немного другим:

4> ping_test:ping().
received ping
received pong
connection to ping timed out

=ERROR REPORT==== 2-Feb-2008::14:30:23 ===
Error in process <0.43.0> with exit 
    value: {ping_timeout,[{ping_test,pong,0}]}

** exception exit: pong
     in function  ping_test:pong/0

 

Не рекомендуется использовать другие механизмы, такие как коды ошибок — идеология "let it crash". Скажем, если функция принимает на вход строку — это проблема вызывающего не передавать туда атомы, числа и прочую хрень.

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


Wednesday, January 30, 2008

Синтаксис Erlang в двух словах.

Чтобы писать веб-приложения на эрланге, надо его хотя бы немного знать, поэтому попробую в пару постов уложить основные понятия.

Тут лучше конечно почитать какой-нибудь более серьезный туториал, начиная с собственно "Getting Started with Erlang" (русский перевод на рсдн), или "Thinking In Erlang". Однако ж, поскольку 30-50 страниц осилить не у каждого поднимется мозг, я здесь изобразил небольшую шпаргалку (не претендующую на полноценное введение).

В принципе Эрланг весьма прост, по крайней мере для тех, кто хоть краем глаза видел какой-нибудь другой функциональный язык.

Для начала самые примитивные вещи:

  • Прежде всего, Эрланг — это язык с динамической типизацией, никакой статической проверки типов тут нет.

  • Литералы-числа в Erlang: 1, 4, 15, 25, 16#1F (в шестнадцатеричной системе), 2#01110110 (в двоичной).
    > 1.
    1
    > 1+2.
    3
    > 1+1.5.
    2.50000
  • Литералы-символы: на самом деле это не отдельный тип, а просто целое число — код символа : $a, $b, $\n $_
    > $a.
    97
    > $\n.
    10
  • Списки задаются в квадратных скобках:
    > [].
    []
    > X = [1,2].
    [1,2].
    > [3 | X].
    [3, 1, 2]
  • Строки — "aba a", "ga a ga".
    > [$a, $b, $c].
     "abc"
    Строки — это на самом деле просто списки символов. Поэтому памяти они занимают немало — в общей сложности 8 байт на один символ строки (на 32-битной машине).
  • Битовые строки (binaries) — эффективное представление бинарных данных
    > <<1, 3, "ba">>.
     <<1,3,98,97>>
    
    Их можно сопоставлять с шаблонами, и быстро передавать туда-сюда. Очень полезная фича Эрланга (правда мы ее использовать сильно не будем).
  • Атомы
    abds
    more
    hello
    ok
    undefined
    
    Начинаются с маленькой буквы, и представляют собой что-то среднее между enum'ами и константными строками, пишутся без кавычек и удобны для разных меток и идентификаторов. Атомы очень похожи на строки, но их нельзя склеивать, вычислять длину и пр., длина их ограничена 256 байтами. Они существенно эффективнее строк в обращении.
  • Еще любые значения еще можно объединять в пары, тройки, четверки, пятерки и так далее, с помощью фигурных скобок: 
    {14, 15} 
    {0, "ba", 3} 
    {$a, [1,2], {3,4}} 
    

Имена переменных всегда начинаются с большой буквы (несмотря на то что они везде называются "переменными", менять их нельзя).

Имена функций и модулей обязательно начинаются с маленькой буквы. Сигнатура функции состоит из ее имени и количества параметров — то есть format/2 и format/3 это разные функции.

Паттерн матчинг (сопоставление с шаблоном) в Erlang выглядит так:

case A of
    Pattern1 -> ...;
    Pattern2 -> ...;
    Pattern3 -> ...;
end

В паттернах можно использовать значок _ который матчит все что угодно (wildcard). В жизни используется как-то так:

case Foo of
    {"HELLO", Name} -> send_response("Hello, " ++ Name);
    "BYE" -> send_response("Bye");
    _ -> send_response("Unknown command")
end

Еще паттерны можно использовать прямо в списке параметров функции:

foo({_, B}) ->
    ...;

foo(X) ->
    ...

что эквивалентно

foo(Y) ->
    case Y of
        {_, B} -> ...
        X -> ...
end.


Плюс, значения можно матчить с помощью знака "равно":

X = {1, 3},
{A, B} = X.

теперь A = 1, а B = 3

Причем, если переменная слева уже определена, в паттерне используется ее значение:

   > B = 4.

Получаем run-time ошибку badmatch, потому что B у нас уже равно трем.

С операторами все почти как обычно:

  • арифметические, +-/*%
  • сравнения =/ , ==, =>, =<
  • склеивание списков ++
  • декомпозиция списков (оператор cons) |
  • битовые band, bor, bxor, bnot

Вот, собственно, практически все. Раздражают в Эрланге поначалу только немного странные правила расстановки точек, запятых и точек с запятой. В двух словах — точка ставится в конце определения функции, запятая — между выражениями в определении функции, а точка с запятой разделяет выражения в case .. of и if.

 


В качестве примера — функция, получающая md5 хэш строки. В стандартной библиотеке уже есть функция md5(), которая получает хэш в виде битовой строки.

> H = erlang:md5("boooo").
<<174,62,131,226,250,179,167,216,104,61,142,239,171,209,231,77>>

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

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

> binary_to_list(H).
[174,62,131,226,250,179,167,216,104,61,142,239,171,209,231,77]

Теперь функция, которая преобразует 4 бита в один hex символ:

hex(V) ->
    if
    V < 10 ->
            $0 + V;
    true ->
        $a + (V - 10)
     end.

Да, if в erlang — лишь упрощенный вариант case .. of, где вместо паттернов — булевые выражения. Так что кейс "true ->" тут играет роль else

Теперь один байт можно преобразовать в hex так:

> E = 31.
> [hex(E bsr 4), hex(E band 16#F)].
"1f"

Ну и с помощью функции lists:foldl применить это ко всей исходной строке:

binary_to_hex(Bin) ->
    lists:foldl(fun (E, Acc) ->
            [hex(E bsr 4) | [hex(E band 16#F) | Acc]] end,
        [],
        lists:reverse(binary_to_list(Bin))).
% returns hex representation of md5 in reverse byte order
md5_hex(Bin) ->
    binary_to_hex(erlang:md5(Bin)).

Тут

  • fun (E, Acc) -> ... end
         это определение лямбда-функции
  • lists:foldl() это известный функциональным программистам foldl.
  • lists:reverse() разворачивает список задом наперед
  • Конструкция вида [A | B] приклеивает в начало списка B элемент A (см. cons).

Итого получаем

>md5_hex("booo").
"1c052f260d1b34423c32e7c7b29026b9"

Весь исходник (utils.erl)

Про ключевую фичу Erlang — поддержку распределенных приложений поговорим в следующей серии.


Crash Course: веб приложения на Erlang [Дисклеймер]

erlang Почему-то зима выдалась крайне скудной на интересные новости.
Однако это не так уж плохо, можно нормально, не отвлекаясь на чтение RSS заняться непосредственно работой, да и появляется время поковырять разнообразные перспективные технологии, в частности — Erlang, неспроста я ему так уделял внимание последнее время.

В общем, по мотивам своих экзерсисов в erlang, я решил написать небольшой туториал, такой crash course про erlang, причем как средство программирования именно веб-приложений. В качестве примера и сверхзадачи я выбрал веб-клиент (прокси) для социального интернет радио last.fm.

Плюс к этому, меня тут даже рекламировали как пишущего про эрланг — будем оправдывать ;)

"Прокси для ласт.фм", мягко говоря, не очень насущная проблема, но, во-первых, кое-где его действительно блокируют, да и удобно — можно слушать радио в любимом плеере, не используя официальный клиент (раньше такая возможность была, сейчас, кажется, ее убрали).

В конце концов можно слушать ласт.фм без установки клиента, например, на КПК/мобильнике с вайфаем.