Как-то раз пришлось мне менять работу. До этого я работал только с языками типа Python, C++, C# и ещё парочкой подобных. А теперь пришлось начать работать с функциональным языком. Первые впечатления были «да что за фигня?». Однако у меня получилось достаточно быстро адаптироваться. Далее я расскажу об основных моментах, к которым пришлось привыкнуть или которые пришлось понять, чтобы начать писать быстро и адекватно.
1. Pattern matching
Это одна из основных фишек языка. Но полностью осознать её, пока не начнешь реально писать на языке немного сложно. Первое что я прочел сопоставление паттернов — это то что благодаря этой штуке можно достаточно просто извлекать данные из структур и связывать их с переменными. Но на самом деле все несколько сложнее. Сопоставление паттернов работает почти везде в данном языке. Приведу три примера иллюстрирующих основные применения, которыми я пользуюсь почти постоянно.
Пример 1. По определению:
{tuple_item_1, tuple_item_2} = tuple
– разбивает 2-х элементный кортеж на две переменных, которые далее можно использовать.
[head_item | tail_list] = list
– разбивает список на первый элемент в списке и список без первого элемента.
Пример 2. Сопоставление в case:
case get_elem(struct) do
{:ok, elem} -> …
{:error, reason} -> …
end
Функция get_elem(struct) возвращает кортеж, и case позволяет сразу извлечь данные и выбрать дальнейшую последовательность действий.
Пример3. Сопоставление функций:
def function_1(params, :ok) do
end
def function_1(params, :error) do
end
def function_1(params, _) do
end
Здесь по сути представлена одна и та же функция принимающая два параметра. Сопоставление паттернов позволяет выбрать, какую именно функцию исполнить.
Немного о работе сопоставления паттернов. Сопоставление идет всегда «сверху-вниз». В данном примере при вызове function_1 от двух параметров, сначала произойдет проверка, что второй параметр равен :ok. Если первая проверка провалится, то произойдет проверка на :error. И если опять нет, то мы в любом случае войдем в третий вариант метода. Нижнее подчеркивание обозначает «любые данные», а также то, что пришедшие данные нас не интересуют, то есть мы их не будем использовать. Если бы function_1(params, _) стояла первой в списке, то программа всегда выбирала бы её, и остальные два метода не работали бы никогда. Если не будет найден нужный паттерн, то вылезет исключение.
2. Pipeline
Это конструкции следующего вида:
param_1
|> func_1()
|> func_2(param_2)
…
С первого взгляда кажется фигня какая-то. Но стоит вспомнить о том, что Elixir – функциональный язык. И в функциональном языке вполне нормально делать вычисления функции от функции, без промежуточных переменных. Пайплайн – всего лишь удобная запись. Сам язык просит для ясности начинать пайплайн с переменной или значения.
Пример выше можно переписать следующим образом:
func_2(func_1(param_1), param_2)
Иначе говоря, пайплайн перенаправляет результат вычисления предыдущей функции в следующую функцию первым аргументом.
3. Отсутствие циклов
В Эликсире нет циклов. Этот факт вызвал наибольший шок у меня, и он наиболее сложен для понимания. Далее идет мое мнение и видение, которое может не совпадать с реальностью и теорией.
Корни этого факта в функциональной парадигме программирования, одно из положений которой гласит, что результат работы программы – это работа функции, которая может вызывать другие функции и программа не предполагает хранение промежуточных состояний. Циклы, в свою очередь предназначены, для многократного изменения внешнего по отношению к циклу состояния.
Заменой циклам служат 2 вещи – рекурсия и библиотечные методы работы с enumerable элементами языка.
Немного ещё о мелочах.
1. В Элексире нет классов, но есть контексты. По сути контексты в некотором роде заменяют классы. Наиболее близкое описание контекста глазами си-шарпера: контексты это нечто среднее между классом и пространством имен в шарпе, причем к пространству имен контекст намного ближе.
2. Атомы. В Элексире есть такое понятие как атом. Атом по сути это нечто вроде «метки». Проще всего к ним относится как к особым строкам. В примерах этой статьи уже было два атома: :ok, :error. Благодаря атомам намного проще осуществлять сопоставление паттернов, и сложные логические конструкции. По сути это константы, у которых значением является их имя. Атом всегда имеет «:» перед именем.
3. Как правильно читать заголовки методов. В Elixir принято обозначать методы следующим образом (особенно часто это видно в документации): &function/2. Читается это как метод с именем «function» и арностью 2. Арность – количество принимаемых аргументов.
Что мне помогло влиться в язык.
Во-первых, это справочник на андройде «Elixir Tutorial». Он хорош тем, что кратко охватывает основные моменты языка и его синтаксиса, и его можно почитать в автобусе. Минус: он на английском, так что подойдет не каждому.
Во-вторых, книга «Введение в Elixir» за авторством Сенлорен С., Эйзенберг Д… В данной книге показывают приемы работы с языком и объясняют их. Легко читается и позволяет значительно улучшить свою работу с языком. Также её можно найти на русском языке.
В-третьих, официальная онлайн документация. Она сделана удобно и позволяет быстро находить нужные разделы/методы, с подробным описанием и примерами.
На этом все.
Список приведенных материалов:
2. Сенлорен С., Эйзенберг Д. Введение в Elixir. Введение в функциональное программирование. – O'Reilly, 2017.
Автор: zeratul47