«Введение в Elixir» — первая книга по Эликсиру на русском
Эликсир — динамический, функциональный язык программирования, спроектированный для создания масштабируемых и легко поддерживаемых приложений. Основан на Erlang VM, эффективной для распределённых, отказоустойчивых систем с низкой задержкой, в то же время с успехом применяемой в веб-разработке и сфере встроенного ПО.
«Введение в Elixir» — стандартный представитель литературы по языкам программирования. Рассказ ведётся последовательно, от основ синтаксиса к продвинутым модулям и техникам. Примеры синтетические, но при этом иллюстрируют текст так, что сразу становится понятно о чём идёт речь. Учитывая, что язык — выразительный, а набор инструментов, доставшийся в наследство от Эрланга впечатляет, скучать не приходится.
Далее рассмотрю каждую из глав подробнее.
Глава 1. Устраиваемся поудобнее
Здесь всё стандартно. Подготовка к работе с языком, объяснение основы основ и коротенький мастер-класс по работе с интерактивной оболочкой.
Глава 2. Функции и модули
Спустя 20 минут работы с книгой, нас уже знакомят с основной прелестью и красотой языка — функцией. Казалось бы, ничего необычного — просто один из элементов языка. Но на деле, функции в Эликсире — его фундамент.
И уже здесь раскрываются первые необычные возможности языка. Например, объединение функций через пайпы, когда подобная конструкция
Enum.map(List.flatten([1, [2], 3]), fn x -> x * 2 end)
превращается в элегантного вида
[1, [2], 3]
|> List.flatten
|> Enum.map(fn x -> x * 2 end)
Или как вам такая особенность? Добавляя комментарии к функциям можно тут же получать полноценную документацию, встроенными в язык средствами.
Глава 3. Атомы, кортежи и сопоставление с образцом
Опыт работы с книгой зависит от того, с какими языками вы уже знакомы. Если ранее вы не сталкивались с функциональными языками или не знакомы с синтаксисом Руби, то уже здесь начнутся откровения.
Во-первых, в Эликсире используется тип данных атом. Вроде бы ничего особенного, элементы этого типа имеют значение, совпадающее с именем.
iex(1)> :test
:test
Но такая простая идея существенным образом (при использовании других механизмов языка) оказывают влияние на стиль программирования на Эликсире.
defmodule Drop do
def fall_velocity(:earth, distance) do
:math.sqrt(2 * 9.8 * distance)
end
def fall_velocity(:moon, distance) do
:math.sqrt(2 * 1.6 * distance)
end
def fall_velocity(:mars, distance) do
:math.sqrt(2 * 3.71 * distance)
end
end
Например, здесь не переопределяется одна и та же функция трижды. На самом деле это 3 разные функции, каждая из которых может быть выбрана через сопоставлении с образцом первого аргумента-атома.
Это основная техника языка, с помощью которой можно творить чудеса. И в книге рассказывается как это делать. Будь то с использованием охранных условий либо более сложных, чем обычные атомы, сопоставлений с образцом.
Глава 4. Логика и рекурсия
Программы на функциональных языках принято писать больше с использованием рекурсии, чем через стандартные циклы. О том, как правильно написать рекурсию, которую сможет оптимизировать компилятор, читайте в этой главе. Благодаря сопоставлению с образцом, которое используется и в охранных условиях, и в логических выражениях, рекурсия получается очень элегантной и необычной.
defmodule Fact do
def factorial(n) do
factorial(1, n, 1)
end
defp factorial(current, n, result) when current <= n do
new_result = result * current
IO.puts("#{current} yields #{new_result}.") factorial(current + 1, n, new_result)
end
defp factorial(_current, _n, result) do
IO.puts("Finished!")
result
end
end
Глава 5. Взаимодействие с человеком
Одним из основных технических отличий Эликсира от Эрланга является полная переработка механизма работы строк. Теперь это не просто списки символов, а полноценный бинарный тип данных, который может поддерживать Юникод. И именно благодаря строкам мы можем вступать во взаимодействие с пользователем.
Глава 6. Списки
Надеюсь, что мне удаётся пробудить в вас интерес к языку. Помимо интересных синтаксических особенностей и классных идей в основе, всё работает потрясающе быстро. Неспроста, Эликсир сравнивают по скорости работы с Го и Растом, а по скорости разработки с Руби.
Эликсир имеет превосходную поддержку списков, длинных последовательностей значений. Они помогают оценить достоинства рекурсии и позволяют выполнить большой объем работы с минимальными усилиями. Хоть это и стандартный тип данных для функциональных языков, здесь есть о чём поговорить.
defmodule Pascal do
def add_row(initial) do
add_row(initial, 0, [])
end
def add_row([], 0, nal) do
[0 | nal]
end
def add_row([h | t], last, new) do
add_row(t, h, [last + h | new])
end
end
Вот такое вот функциональное программирование с человеческим лицом.
Глава 7. Пары имя/значение
Меня порой спрашивают. Раз в Эликсире нет объектов, то как же нам работать со структурированными данными? Кортежи и списки — это хорошо, но они не позволяют достать значение по имени. Эликсир, хоть и молодой, но уже зрелый (даже продакшн реди) язык. Поэтому разработчики предусмотрели механизмы для решения этой задачи. В Эликсире есть типы данных, в которых можно получать значение по ключу. И в этой главе рассматривается именно они — от словаря до структуры.
Глава 8. Функции высшего порядка и генераторы списков
Функции высшего порядка, или функции, принимающие в аргументах другие функции, помогают Эликсиру проявиться во всем его блеске. Нельзя сказать, что в других языках невозможно реализовать подобные функции, — это возможно почти в любых языках, но в Эликсире функции высшего порядка интерпретируются как естественная часть языка, а не как искусственное и чужеродное образование.
Не самая интересная глава, но узнать об этих возможностях будет не лишним.
Глава 9. Процессы
Если у вас неплохой опыт программирования, то до текущего момента вам могло быть скучновато. Спешу вас обрадовать, самое интересное о киллер-фичах Эликсира начинается прямо сейчас!
При описании Эликсира в качестве преимущества обычно упоминают некие процессы. Говорят, что они позволяют добиться распределённости, повышают скорость работы программы, используются при масштабировании и горячей замене кода, но совершенно не ясно что они собой представляют и как выглядят в рамках самого языка.
На самом деле, процессы — это ещё одна организационная единица программы, наравне с функциями и модулями, которая является независимым компонентом, способным принимать и отправлять сообщения. Работающая программа — это набор процессов.
Начинается разговор в этой главе с интерактивной оболочки, которая также является процессом. О процессах говорится много и подробно, также рассматриваются и дополнительные инструменты для работы с ними.
Глава 10. Исключения, ошибки и отладка
С отладкой в Эликсире пока не всё гладко, так как не существует полноценных нативных инструментов. Самое популярное —
Pry
(который пришёл из Руби) и :dbg
(из Эрланга). На самом деле, ими вполне комфортно пользоваться, и в книге даётся неплохой разбор второго из них.Глава 11. Статический анализ, спецификации типов и тестирование
В программировании известны три основных класса ошибок: синтаксические ошибки, ошибки времени выполнения и семантические ошибки. Компилятор Эликсира обнаруживает и сообщает о синтаксических ошибках. Остаются логические ошибки, когда вы требуете от Эликсира одно, имея в виду другое. Журналирование и трассировка могут помочь в поиске подобных ошибок, но всё же лучше постараться предотвратить их появление с самого начала. И в этом вам помогут статический анализ, спецификации типов и модульное тестирование. Обо всём этом рассказывается в этой главе.
Эликсир имеет очень нетривиальные возможности в этих областях. Например, встроенные в документацию тесты:
defmodule Drop do
@doc """
Вычисляет скорость падающего объекта на указанном объекте
планемо (объекте с планетарной массой)
iex(1)> Drop.fall_velocity(:earth, 10)
14.0
iex(2)> Drop.fall_velocity(:mars, 20)
12.181953866272849
iex> Drop.fall_velocity(:jupiter, 10)
** (CaseClauseError) no case clause matching: :jupiter
"""
def fall_velocity(planemo, distance) do
gravity = case planemo do
:earth -> 9.8
:moon -> 1.6
:mars -> 3.71
end
:math.sqrt(2 * gravity * distance)
end
end
Теперь, можно быть уверенными, что документация всегда будет актуальной!
Глава 12. Хранение структурированных данных
Как бы нам ни хотелось иметь чистые функции, на практике это невозможно, ведь данные нужно где-то хранить. Для этого у Эрланга есть куча встроенных инструментов, которыми к счастью можно пользоваться и из Эликсира. В этой главе рассматриваются ETS и Mnesia.
Просто представьте, что вам больше не понадобится Редис, в качестве внешней зависимости. Ведь его «заменитель» встроен прямо в язык и поддерживается из коробки.
Глава 13. Основы OTP
Все, приходящие в мир Эрланга и Эликсира, слышат о неком OTP, который обсуждают более опытные коллеги. На подкорке откладывается, что это какая-то очень крутая штука, невероятно полезная и просто мастхев в повседневной разработке. Но что же это такое, абсолютно не ясно.
Оказывается, OTP — это фреймворк для написания распаралелленных и высоконадёжных приложений. Он использует все фишки виртуальной машины Эрланга и позволяет программистам задумываться только о бизнес-логике, а не о деталях реализации.
Самыми популярными модулями являются Supervisor и GenServer. Выглядят они так:
defmodule DropServer do
use GenServer
defmodule State do
defstruct count: 0
end
def start_link do
GenServer.start_link(__MODULE__, [], [{:name, __MODULE__}])
end
def init([]) do
{:ok, %State{}}
end
def handle_call(request, _from, state) do
distance = request
reply = {:ok, fall_velocity(distance)}
new_state = %State{count: state.count + 1}
{:reply, reply, new_state}
end
def handle_cast(_msg, state) do
IO.puts("So far, calculated #{state.count} velocities.")
{:noreply, state}
end
def handle_info(_info, state) do
{:noreply, state}
end
def terminate(_reason, _state) do
{:ok}
end
def code_change(_old_version, state, _extra) do
{:ok, state}
end
def fall_velocity(distance) do
:math.sqrt(2 * 9.8 * distance)
end
end
Узнать о них подробнее можно тут и тут. Либо в текущей главе книги.
А ещё Эликсир предлагает штуку, о которой мечтают все эрлангисты — утилиту Mix.
Её обзор тоже есть в книге.
Глава 14. Расширение языка Elixir с помощью макросов
Следующая возможность, которая выгодно отличает Эликсир от Эрланга — продвинутое метапрограммирование. Оно реализовано через создание макросов. Внешне макросы очень похожи на функции, отличаясь только тем, что начинаются с инструкции
defmacro
вместо def
. Однако макросы действуют совершенно иначе, чем функции. Благодаря этому можно создавать крутой DSL, не усупающий рубишному.Глава 15. Phoenix
Ну и завершается книга такой важной темой как Феникс. Это веб-фреймворк, который так и хочется сравнить с Рельсами. Феникс унаследовал рельсовую философию и некоторые подходы, хотя сейчас постепенно отодвигается в свою сторону. Но нельзя не отметить, что Феникс является таким же двигателем для Эликсира, как и в своё время Рельсы для Руби.
Вводный курс по Фениксу можно прочитать в книге либо в этой серии статей, которую я перевожу для Хабра.
Выводы
Книга исключительно годная. Я уже несколько месяцев занимаюсь проектом Вунш, где мы знакомим русскоязычных читателей с прекрасным Эликсиром. И мне всё равно было интересно читать книгу, удалось почерпнуть нюансы при работе со знакомыми частями языка и узнать о других возможностях, с которыми не приходилось раньше сталкиваться.
В тексте расставлены ссылки на интересные статьи по Эликсиру, которые мы переводим в рамках Вунша. Если вы хотите еженедельно получать полезную информацию по Эликсиру, подписывайтесь на нашу рассылку.
Что важно отметить, и за что я переживал больше всего — это перевод. И к счастью, он получился довольно удачным. Надмозг практически отсутствует, а переводы терминов, хоть и хотелось бы оставить чуть более англифицированными, слух не режут. Такая дань академичности вполне уместна.
Благодарю ДМК Пресс за издание «Введения в Elixir» и оставляю промокод на 25% специально для хабровчан Elixir_Habr (действителен до 14 числа).
Надеюсь, мой обзор вам пригодится. Всем хорошего дня и побольше полезных книг!