Spec-Zone.ru › Elixir 1.18

Исходный код Присвоение и изъятие

Это руководство призвано познакомить вас с методами метапрограммирования, доступными в 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.36.1) для языка программирования Elixir

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

Spec-Zone.ru

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