Elixir и IEx. С чего начать?

Если вы только приступили к знакомству с Elixir, то эта статья поможет решить несколько основных проблем, с которыми сталкиваются все новички, и как можно скорее переключиться на изучение более интересных особенностей языка.

1. Как выйти из меню shell-break: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded

«У меня есть 10 лишних минут, почему бы не покопаться в Erlang/Elixir?». Трудно сосчитать, сколько раз я уже говорил себе эту фразу, застревал на определённом этапе разработки и переключался на другие более насущные задачи. Раз пятнадцать точно. А может, и больше.

Предположим, в процессе написания кода разработчик обнаружил, что совершил ошибку. Для отмены он нажмёт Ctrl+C, после чего на экране появится следующее меню, которое в Erlang называется shell-break:

iex(2)> x = fn (a, b) ->
...(2)>   c + b
BREAK: (a)bort (c)ontinue (p)roc info (i)nfo (l)oaded
       (v)ersion (k)ill (D)b-tables (d)istribution

Понадобятся здесь только две опции: (c) и (a). (c)ontinue вернёт обычный режим ввода, но для того, чтобы вернуться, придётся нажать клавишу Enter дважды, что совершенно неочевидно. Опция (a) осуществит выход из интерактивной оболочки.

Такое поведение прописано в алгоритме виртуальной машины BEAM, исполняющей программы на Elixir и Erlang. Остальные опции способны предоставить огромное количество информации, которая определённо окажется полезной при проведении отладки самой BEAM, но не будем сейчас забивать этим голову.

2. Как получить информацию о модуле в интерактивной оболочке

Что мне нравится в Ruby, так это возможность вызвать метод methods в любом объекте и получить список всех доступных для объекта методов. Когда я только начинал копаться в Ruby, это действие показалось мне невероятно удобным, и я до сих пор время от времени им пользуюсь.

Как же осуществить подобное в Elixir? Module.module_info. Пример:

# defined in foo/bar/awesome.ex
defmodule Awesome do
  def very_cool(arg) do
    # ...
  end

  def beast_is_the_best_of_the_x_men, do: true
end
iex(4) Awesome.module_info(:exports)
[:beast_is_the_best_of_the_x_men/0, :very_cool/1]

Функция module_info без всяких аргументов возвращает список ключевых слов с соответствующими им ключами: [:module, :attributes, :compile, :native, :md5]. Любое из этих слов можно передать в качестве аргумента, чтобы поместить возвращаемое значение в область видимости.

Конечно, далеко не всё, что есть в Ruby, можно реализовать в Elixir.

3. Как определить функции в IEx

В интерактивной оболочке Ruby (и практически любого другого языка) можно определять функции верхнего уровня. Однако, если осуществить в IEx подобное:

iex(9)> def square(x) do
...(9)>   x * x
...(9)> end

То получится следующее:

** (ArgumentError) cannot invoke def/2 outside module
    (elixir) lib/kernel.ex:4297: Kernel.assert_module_scope/3
    (elixir) lib/kernel.ex:3299: Kernel.define/4
    (elixir) expanding macro: Kernel.def/2
             iex:9: (file)

Существуют два способа определения функций в IEx. Первый — объявить анонимную функцию. Придётся вызывать её достаточно странным способом (такой синтаксис в Ruby безошибочен, но перегружен):

iex(8) square = fn (x) -> x * x end
iex(9) square.(20)
400

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

iex(10) defmodule Necessary do
...(10)   def square (x) do
...(10)     x * x
...(10)   end
...(10) end
iex(10) Necessary.square(20)
400

4. Как скомпилировать код в файл

Допустим, имеется некий код, представляющий собой модуль. Можно скомпилировать его с помощью команды c, указав после неё путь к файлу:

iex(3)> c "foo/bar/awesome.ex"
[Awesome]

После этого все модули, описанные в файле awesome.ex станут доступными для использования.

5. Как добавить новые функции в модуль

Модули в Elixir не являются открытыми. Это значит, что, если попробовать определить уже существующий модуль defmodule AlreadyExistingModule ..., он перепишется и будет содержать только новые функции. А ещё это значит, что «проманкипатчить» основной модуль тоже не получится.

Если же есть необходимость добавить новую функцию или переопределить старую, и при этом код хранится в файле, то можно дописать в него нужные строки и провести его компиляцию повторно командой r ModuleName. Таким образом, при всех последующих вызовах методы будут содержать обновлённый код.

6. Как узнать о других доступных командах (бонус)

Для этого просто воспользуйтесь командой h.

По синтаксису и набору инструментальных средств Elixir крайне схож с Ruby, но наряду с этим у него имеются и некоторые отличные особенности. Нужно заметить, что интерес к Elixir растёт, и это не может не радовать. Так почему бы вам не опробовать все описанное выше прямо сейчас?

© 2020 / Россия Любые мысли и вопросы пишите на elixir@wunsh.ru.