Данная статья рассчитана на тех, кто уже немного познакомился с метапрограммированием в эликсире, но так и не понял, где же применять и творить магию.
Повторим: quote и unquote
Выражения в эликсире представляют собой кортежи. Эти кортежи состоят из трех частей: название функции, метаданные и аргументы функции.
Например блок указанный ниже вернет список кортежей для каждого выражения, для List.last, для пайплайна и для Enum.sum.
quote — возвращает внутренние структуры выражений
unquote — разворачивает внутри этих структур значения переданных переменных, тем самым позволяя указать в структуре кортежа название нужной функции или значение аргументов. За счет этого в эликсире обеспечивается метапрограммирование.
Рассмотрим несколько примеров, которые помогут лучше понять область применения макросов в эликсире.
Пример №1
Внутри модулей мы часто используем значения из конфига приложения, обычно это делается командой:
Application.get_env(:my_awesome_app, __MODULE__)[:link]
Где :my_awesome_app — ключ otp_app;
__MODULE__ — название модуля;
:link — параметр конфига (ключ).
Эта довольно громоздкая конструкция начинает повторяться в разных модулях, что понижает читаемость кода. Тут нам на помощь приходят макросы.
Объявим на уровне приложения следующий макрос:
Теперь в модулях приложения MyAwesomeApp можно получать значения конфига простой командой:
MyAwesomeApp.get_config(:link)
Функция host вернет значение host из конфига модуля Foo.
Пример №2
Давайте улучшим стандартный Logger, чтобы он сохранял чуть больше информации, чем обычно. Напишем абстракцию, которая дополнит стандартную запись в лог информацией о хосте и функции, из которой мы производим логирование.
Создадим модуль MyLogger
Теперь, когда мы будем пользоваться функцией info нашего Logger’а, в логах будет выводиться дополнительная информация.
Например при вызове Foo.bar в логах будет запись:
23:06:09.800 [info] [host: macbook_2017, fun: bar/1] Actions completed
Пример №3
С помощью метапрограммирования мы можем динамически создавать функции, хоть макросы здесь и не пригодятся.
Напишем модуль, который возвращает целые числа от 1 до 99, где функция будет числовым литералом.
Теперь можем получать цифровые значения всего лишь обратившись к соответствующей функции:
Number.sixty_one # 61
Number.forty_two # 42
Number.one # 1
Надеюсь данная статья помогла лучше освоить макросы и метапрограммирование в эликсире. Если остались какие-то вопросы, буду рад ответить на них в комментариях.