Написал свой первый простенький скрипт на Erlang

25 июля 2012

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

Об Erlang много пишут в этих ваших интернетах, дескать он нашел широкое применение в разработке высоконагруженных отказоустойчивых приложений. Кроме того, недавно вышел русский перевод книги «Erlang Programming». Данные обстоятельства и послужили мне стимулами для изучения Erlang.

Краткая информация о языке:

  • Erlang — функциональный язык программирования (правда, не чистый, как Haskell) со строгой динамической типизацией;
  • Программы на Erlang транслируются в байт-код, который затем выполняется виртуальной машиной;
  • Аргументы передаются функциям только по значению, глобальные переменные и указатели отсутствуют, присвоение значений переменным допускается только при их объявлении, управление памятью происходит автоматически;
  • Прочие фишки функционального программирования — сопоставление с образцом, «лямбды», замыкания, REPL-разработка и тп, правда нет поддержки каррирования и ленивых вычислений;
  • Есть поддержка исключений;
  • Легковесные процессы, отсутствие разделяемой памяти (чуть подробнее об этом будет рассказано ниже);
  • Развитый инструментарий — EUnit и Common Test для написания тестов, EDoc для документирования кода, Dialyzer для проверки типов и TypeEr для автоматического вывода типов (в языке с динамической типизацией!), Rebar для сборки проектов и установки зависимостей, dbg для отладки, а также xref, erl_tidy и другие;
  • Большое количество годных библиотек и фреймворков, а также исчерпывающая документация к ним;
  • Широкая область применения — на Erlang написаны XMPP-сервер ejabberd, СУБД Mnesia, CouchDB, Couchbase, Scalaris и Riak, система обмена сообщениями RabbitMQ, программа 3D-моделирования Wings 3D, видеостриминговый сервер Erlyvideo, веб-серверы Cowboy и Yaws, веб-фреймворк Nitrogen, серверная часть игры Call of Duty, Map-Reduce фреймворк Disco, CMS Zotonic, распределенная система нагрузочного тестирования Tsung, сервер API системы управления конфигурацией Chef и другие;
  • Используется в Yahoo, Amazon, Facebook, Motorola, Ericsson, Яндексе, WhatsApp, Nokia, GitHub, Blizzard, Hypercomments и не только;

Важной отличительной особенностью Erlang является модель легковесных процессов. Эти процессы не являются процессами операционной системы. Только виртуальная машина знает об их существовании и она же распределяет между ними процессорное время.

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

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

Как я уже когда-то писал, при изучении нового языка программирования обычно я беру прототип программы на уже знакомом мне языке, а затем переписываю его. При изучении Erlang в качестве такого прототипа я выбрал программу, решающую задачу о дне системного администратора. В результате у меня получилась такая библиотека для работы с датами:

-module(dates).
-import(lists).
% -compile(export_all).
-export([is_leap_year/1, days_in_year/1, days_in_month/2,
         days_from_epoch/3, day_of_week/3]).

-define(DEBUG, false).

-define(JANUARY, 1).
-define(FEBRUARY, 2).
-define(APRIL, 4).
-define(JUNE, 6).
-define(SEPTEMBER, 9).
-define(NOVEMBER, 11).
-define(DECEMBER, 12).

-ifdef(DEBUG).
  -define(CHECK_MONTH(Month),
      case (Month >= ?JANUARY) and (Month =< ?DECEMBER) of
        false -> erlang:error(badarg);
        _ -> ok
      end
    ).

-else.
  -define(CHECK_MONTH(_), ok).
-endif.

is_leap_year(Y) ->
  if
    Y rem 400 == 0 -> true;
    Y rem 100 == 0 -> false;
    Y rem 4 == 0 -> true;
    true -> false
  end.

days_in_year(Y) ->
  case is_leap_year(Y) of
    true -> 366;
    false -> 365
  end.

days_in_month(?FEBRUARY, IsLeapYear) ->
  if
    IsLeapYear -> 29;
    true -> 28
  end;
days_in_month(Month, _) ->
  ?CHECK_MONTH(Month),
  case lists:member(
         Month,
         [?APRIL,?JUNE,?SEPTEMBER,?NOVEMBER]) of
    true -> 30;
    _ -> 31
  end.

months_before(?JANUARY) -> [];
months_before(Month) ->
  ?CHECK_MONTH(Month),
  [ Month - 1 | months_before(Month - 1) ].

% число дней с 1-го января 1-го года (не включая)
days_from_epoch(Year,Month,Day) ->
  ?CHECK_MONTH(Month),
  IsLeap = is_leap_year(Year),
  YDays = lists:map(fun days_in_year/1, lists:seq(1,Year-1)),
  MDays = lists:map(
    fun(M) -> days_in_month(M, IsLeap) end,
    months_before(Month)),
  lists:sum(YDays) + lists:sum(MDays) + Day-1.

day_of_week(Year,Month,Day) ->
  days_from_epoch(Year,Month,Day) rem 7.

А вот и скрипт main.erl, определяющий, на какое число июля приходится день системного администратора в 2012-ом году:

#!/usr/bin/env escript

-import(lists).
-import(dates).

-define(JULY, 7).
-define(FRIDAY, 4).
sys_admin_day_in_year(Y) ->
  Pred = fun(D) -> dates:day_of_week(Y, ?JULY, D) == ?FRIDAY end,
  lists:last(lists:filter(Pred, lists:seq(1,31))).

main(_Args) ->
  io:format("~w\n", [ sys_admin_day_in_year(2012) ]).

Установка Erlang и запуск скрипта:

sudo apt-get install erlang
chmod u+x ./main.erl
./main.erl

Тут, конечно, никаким хайлоадом или распределенностью даже не пахнет, но, думаю, синтаксис я уже более-менее освоил. Запустив скрипт, мы узнаем, что день сисадмина в этом году приходится на 27-е июля. Не забудьте поздравить знакомых админов!

Узнать больше об Erlang можно из книги Learn You Some Erlang for Great Good. Это аналог книги «Изучай Haskell во имя добра», только про Erlang.

Дополнение: Впечатления от Erlang после года работы с ним

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.