Проблема здесь заключается во вложенных кавычках: закрытый макрос должен возвращать выражение в двойных кавычках (поскольку для его вызова из внешней области необходимо явно unquote
, а макрос по-прежнему должен возвращать выражение в кавычках.)
Sidenote: ваше обновление раздел неправильный ;вы можете заметить, что "hello"
печатается на этапе компиляции, а именно, когда use Foo
компилируется.Это связано с необходимостью двойных кавычек: код в разделе update выполняет IO.puts
при выполнении макроса unquote
в __using__
.
С другой стороны, my
следует указывать только один раз.Это может быть достигнуто с явным цитированием AST , передавая msg
там как есть :
defmodule Foo do
defmacrop debug(msg) do
quote bind_quoted: [msg: msg] do
{
{:., [], [{:__aliases__, [alias: false], [:IO]}, :puts]},
[],
[msg]} # ⇐ HERE `msg` is the untouched argument
end
end
defmacro __using__(_) do
quote do
def hello do
my = "testme"
unquote(debug(quote do: my))
end
end
end
end
defmodule Bar do
use Foo
end
Bar.hello()
#⇒ "testme"
Мне не удалось достичь той же функциональности сварианты при звонке на Kernel.SpecialForms.quote/2
;единственный доступный связанный параметр - unquote
для настройки , без кавычек внутри вложенных кавычек, в то время как нам нужна полная противоположность.
Sidenote: ниже не означаетработать, и я ожидаю, что это будет ошибка в Kernel.SpecialForms.quote/2
реализации.
quote bind_quoted: [msg: msg] do
quote bind_quoted: [msg: msg], do: IO.puts(msg)
end
FWIW: я подал проблему .
Iполагаю, что это может быть хорошим запросом к ядру Elixir, чтобы разрешить опцию, которая отключает дополнительные цитирование .
Sidenote 2: следующие работы ( самый лаконичный подход ):
defmacrop debug(msg) do
quote bind_quoted: [msg: msg] do
quote do: IO.puts(unquote msg)
end
end
Таким образом, вы можете избежать использования явного AST и просто использовать вышеизложенное.Я оставляю ответ без изменений, поскольку работа с AST также является очень хорошим вариантом, который следует использовать как кувалду / последнее средство, которое всегда работает.
Если IO.puts
не желаемая цель, вы можете вызвать quote do: YOUR_EXPR
для того, что вы хотите иметь в макросе debug
:
quote do: to_string(arg)
#⇒ {:to_string, [context: Elixir, import: Kernel], [{:arg, [], Elixir}]}
и вручную заключить в кавычки arg
в результате:
# ✗ ⇓⇓⇓ {:arg, [], Elixir}
# ✓ ⇓⇓⇓ arg
{:to_string, [context: Elixir, import: Kernel], [arg]}
Вот как я получил AST по вашему первоначальному запросу (IO.puts
.)