Основы Erlang: синтаксис и пунктуация

Мои теоретичесие рассуждения о месте Erlang в современном мире Интернете Вы можете почитать в отдельной статье. Если сомневаетесь интересно Вам это все или нет - то прочтите сначала её. Сегодня я постараюсь вернуться с небес на землю и пройтись по азам этого пугающего многих языка программирования. Коротко и по делу.

Установка ничем особым не выделяется, дистрибутив рекомендую брать отсюда, если до сих пор пользуетесь отсутствующей в списке ОС - лучше сначала исправить этот факт.

После установки в $PATH окажутся исполняемые файлы:

  • erl - одновременно интерактивная консоль и запуск приложений;
  • erlc - компилятор в байт-код для виртуальной машины BEAM или нативный код посредством HiPE, напрямую использовать не придется практически.

Со всем что будет обсуждаться в этой статье можно эксперементировать просто в интерактивной консоли, которая запускается просто командой erl без аргументов.

Пунктуация

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

  • Все функции заканчиваются точкой
  • После каждого выражения в функции - запятая;
  • Все ветви кода (case, if, ...), кроме последней, заканчиваются точкой с запятой
  • После заголовка функции и условий ветвления пишется стрелка->

Маленькая демонстрация:

foo(X, Y) ->
  Z = X * X,
  if
    Y > 0 ->
      Z + Y;
    true ->
      Z - Y
  end.

К слову, функции возвращают результат выполнения последнего выражения, в данном случае оно представляет собой весь блок if, а end обозначает его окончание (не функции).

Синтаксис

  • Foo - все что начинается с английской заглавной буквы - переменная, специально объявлять ничего не нужно
  • _ - сам знак нижнего подчеркивания и все что с него начинается - особый случай переменной, значение которой не значимо для программы и при первой возможности "выкидывается"
  • Цифры в основном как обычно, есть научная нотация в духе 1.23e4 (1.23 умножить на 10 в степени 4) и системы исчисления с другим основанием, скажем двоичная - 2#101010
  • foo - с строчной буквы начинаются атомы, по сути константы, используются повсеместно:
    • названия функций и модулей
    • true и false - булевые значения
    • ok - типичный результат успешный результат выполнения
  • ?FOO - хоть официально и называются константами, но по сути - макросы, перед компиляцией заменяются на заранее определенный кусок кода 
  • {foo, bar} - кортеж, набор данных фиксированной длины
  • [foo, bar] - простой однонаправленный список произвольной длины
  • "foo" - текстовая строка, представленная в виде однонаправленного списка (что не эффективно с точки зрения потребления памяти, до 4 байт на символ)
  • <<"foo">> - бинарная строка, может содержать что угодно, в.т.ч. и текст; все что не цифры по возможности лучше хранить в этом типе данных.

Сопоставление (pattern matching)

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

{ok, Result} = foo().

Если в функции foo все прошло нормально, то она возвращает, например {ok, 123}, и переменной Result окажется лишь значение 123.

Если же возникла какая-то проблема, то она вернет что-то другое, скажем {error, timeout}, приложение столкнется с несоответствием левой и правой части (атомы ok и error разные) и прекращает свое выполнение (если бы было чего выполнять).

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

Списки

Со списками есть три особые операции:

  • [Head | Tail ] = [1, 2, 3, 4] - вытащить элемент с головы списка, работает по принципу сопоставления, в Head окажется 1, а в Tail - [2, 3, 4]
  • [1, 2] ++ [3, 4] - конкатенация, результатом будет [1, 2, 3, 4]
  • [N * N || N <- [1, 2, 3], N > 1] - выглядит замороченно, по сути это обычный отображение (map) с фильтрацией (filter) - то есть выражение перед || применяется к каждому элементу списка, значение которых попадает в переменную N, а после запятой - условие, накладываемое на N; таким образом результат будет [4, 9]

Бинарные строки

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

  • Binary = <<Integer:64>> - преобразовать целое число Integer в бинарную строку Binary длиной 64 бита (для примера, может быть любой
  • <<Integer1:32, Integer2:32>> = Binary - распокавать обратно бинарную строку в целые числа, но уже два по 32 бита; чем-то похоже на операцию [H | T] у списков, но намного более гибко
  • Binary3 = <<Binary1/binary, Binary2/binary>> - конкатенация бинарных строк, результат окажется в Binary3
  • <<<<(N * N)/integer>> || <<N>><= <<1, 2, 3>>, N > 1 >> - аналог последнего примера для списков, только для бинарных данных; результат аналогичен - <<4, 9>>; к слову официально это называется binary comprehensions, а для списков - list comprehensions

Заключение

Очень многое пришлось опустить, иначе самое главное затерялось бы, да и объем статьи сильно вырос. Подробности всегда можно найти на официальном сайте, в man'ах, да и просто погуглив. 

Удачного освоения Erlang!

22 июня 2012 |  Иван Блинков  |  Erlang