Вызов частного макроса в блоке кавычек - PullRequest
0 голосов
/ 23 мая 2018

Я пытаюсь вызвать приватный макрос в блоке кавычек, используя переменную, определенную в самом блоке кода.Это псевдокод, показывающий, что я хотел бы сделать (не работает)

defmodule Foo do
  defmacrop debug(msg) do
    quote bind_quoted: [msg: msg], do: IO.puts(msg)
  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()

И это будет преобразовано (на мой взгляд) во время компиляции в:

defmodule Bar do
  def hello do
    my = "testme"
    IO.puts(my)
  end
end

Есть ли способ достичь этого?Я изо всех сил пытаюсь найти любую документацию, связанную с этим.

Обновление

Я обнаружил, что:

defmodule Foo do
  defmacrop debug() do
    quote do: IO.puts("hello")
  end

  defmacro __using__(_) do
    quote do
      def hello do
        my = "testme"

        unquote(debug())
      end
    end
  end
end

Правильно преобразуется в то, что янужно, но я изо всех сил пытаюсь найти способ передать переменную как есть, чтобы она стала IO.puts(my)

1 Ответ

0 голосов
/ 25 мая 2018

Проблема здесь заключается во вложенных кавычках: закрытый макрос должен возвращать выражение в двойных кавычках (поскольку для его вызова из внешней области необходимо явно 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.)

...