Spec-Zone.ru › Elixir 1.16

Source Выражения с цитированием и раскрытием

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

Цитирование

Основным строительным блоком программы 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. Как это утверждение согласуется с цитируемыми выражениями?

Раскрытие

Цитирование заключается в получении внутреннего представления некоторой части кода. Однако иногда может быть необходимо вставить какую-либо другую часть кода во внутреннее представление, которое мы хотим получить.

Например, представьте, что у вас есть переменная 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]"

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

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

Как мы видели в начале этой главы, только некоторые значения являются допустимыми цитируемыми выражениями в 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.32.2) для языка программирования Elixir

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

Spec-Zone.ru

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