Spec-Zone.ru › Elixir 1.17

Исходный код Оператор quote и unquote

Это руководство призвано представить метапрограммирования методы, доступные в Elixir. Возможность представления программы Elixir своими собственными структурами данных лежит в основе метапрограммирования. В этой главе мы начнём с изучения этих структур и связанных quote/2 и unquote/1 конструкций, чтобы затем рассмотреть макросы в следующем руководстве и, наконец, создать свой собственный язык предметной области.

Оператор quote

Основным строительным блоком программы Elixir является кортеж с тремя элементами. Например, вызов функции sum(1, 2, 3) внутренне представлен как:

{:sum, [], [1, 2, 3]}

Вы можете получить представление любого выражения, используя макрос quote/2:

iex> quote do: sum(1, 2, 3)
{:sum, [], [1, 2, 3]}

Первый элемент — имя функции, второй — список ключевых слов, содержащих метаданные, а третий — список аргументов.

Операторы также представлены такими кортежами:

iex> quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}

Даже карта представлена как вызов %{}:

iex> quote do: %{1 => 2}
{:%{}, [], [{1, 2}]}

Переменные представлены такими тройками, с той лишь разницей, что последний элемент — атом, а не список:

iex> quote do: x
{:x, [], Elixir}

При цитировании более сложных выражений мы видим, что код представлен такими кортежами, которые часто вложены друг в друга в структуре, напоминающей дерево. Многие языки назвали бы такие представления деревом абстрактного синтаксиса (AST). Elixir называет их цитируемыми выражениями:

iex> quote do: sum(1, 2 + 3, 4)
{:sum, [], [1, {:+, [context: Elixir, import: Kernel], [2, 3]}, 4]}

Иногда при работе с цитируемыми выражениями может быть полезно получить текстовое представление кода обратно. Это можно сделать с помощью Macro.to_string/1:

iex> Macro.to_string(quote do: sum(1, 2 + 3, 4))
"sum(1, 2 + 3, 4)"

В целом, кортежи выше структурированы следующим образом:

{atom | tuple, list, list | atom}
  • Первый элемент — это атом или другой кортеж в том же представлении;
  • Второй элемент — это список ключевых слов, содержащих метаданные, такие как числа и контексты;
  • Третий элемент — это либо список аргументов для вызова функции, либо атом. Когда этот элемент является атомом, это означает, что кортеж представляет переменную.

Помимо вышеописанного кортежа, существует пять литералов Elixir, которые при цитировании возвращают сами себя (а не кортеж). Это:

:sum         #=> Atoms
1.0          #=> Numbers
[1, 2]       #=> Lists
"strings"    #=> Strings
{key, value} #=> Tuples with two elements

Большая часть кода Elixir имеет прямое отображение на его внутреннее цитируемое выражение. Мы рекомендуем вам попробовать различные примеры кода и посмотреть, что из этого получится. Например, во что расширяется String.upcase("foo")? Мы также узнали, что if(true, do: :this, else: :that) эквивалентно if true do :this else :that end. Как это утверждение согласуется с цитируемыми выражениями?

Оператор unquote

Цитирование — это получение внутреннего представления определённого фрагмента кода. Однако иногда может потребоваться внедрить другой фрагмент кода в представляемое нами выражение.

Например, предположим, у вас есть переменная, названная number, которая содержит число, которое вы хотите внедрить в цитируемое выражение.

iex> number = 13
iex> Macro.to_string(quote do: 11 + number)
"11 + number"

Это не то, что мы хотели, так как значение переменной number не было внедрено, и number было цитировано в выражении. Для того, чтобы внедрить значение переменной number, необходимо использовать unquote/1 внутри цитируемого представления:

iex> number = 13
iex> Macro.to_string(quote do: 11 + unquote(number))
"11 + 13"

unquote/1 можно использовать даже для внедрения имён функций:

iex> fun = :hello
iex> Macro.to_string(quote do: unquote(fun)(:world))
"hello(:world)"

В некоторых случаях может потребоваться внедрить несколько значений в список. Например, предположим, у вас есть список, содержащий [1, 2, 6], и мы хотим внедрить [3, 4, 5] в него. Использование unquote/1 не даст желаемого результата:

iex> inner = [3, 4, 5]
iex> Macro.to_string(quote do: [1, 2, unquote(inner), 6])
"[1, 2, [3, 4, 5], 6]"

Вот где пригождается unquote_splicing/1:

iex> inner = [3, 4, 5]
iex> Macro.to_string(quote do: [1, 2, unquote_splicing(inner), 6])
"[1, 2, 3, 4, 5, 6]"

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

Экранирование

Как мы видели в начале этой главы, только некоторые значения являются допустимыми цитируемыми выражениями в Elixir. Например, карта — это не допустимое цитируемое выражение. Так же, как и кортеж с четырьмя элементами. Однако такие значения могут быть выражены как цитируемое выражение:

iex> quote do: %{1 => 2}
{:%{}, [], [{1, 2}]}

В некоторых случаях вам может потребоваться внедрить такие значения в цитируемые выражения. Для этого нам нужно сначала экранировать эти значения в цитируемые выражения с помощью Macro.escape/1:

iex> map = %{hello: :world}
iex> Macro.escape(map)
{:%{}, [], [hello: :world]}

Макросы получают цитируемые выражения и должны возвращать цитируемые выражения. Однако иногда во время выполнения макроса вам может потребоваться работать со значениями, и потребуется провести различие между значениями и цитируемыми выражениями.

Другими словами, важно различать обычное значение Elixir (например, список, карту, процесс, ссылку и т. д.) и цитируемое выражение. Некоторые значения, такие как целые числа, атомы и строки, имеют цитируемое выражение, равное самому значению. Другие значения, такие как карты, нужно явно преобразовать. И наконец, значения, такие как функции и ссылки, вообще не могут быть преобразованы в цитируемое выражение.

При работе с макросами и кодом, который генерирует код, ознакомьтесь с документацией модуля Macro, который содержит множество функций для работы с AST Elixir.

В этом введении мы заложили основу для написания нашего первого макроса. Вы можете узнать об этом в следующем руководстве.

← Предыдущая страница Антипаттерны метапрограммирования
Следующая страница → Макросы

Скачать версию ePub

Создано с использованием ExDoc (v0.34.1) для языка программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.17.2/quote-and-unquote.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API