Использование переменных, созданных Macro.var - PullRequest
0 голосов
/ 12 октября 2018

Я пытаюсь "шаблонизировать" некоторый избыточный код.Я могу заставить его генерировать предупреждения компилятора о неиспользуемых переменных, но я получаю «недопустимые выражения в кавычках» и пытаюсь использовать те же самые переменные.

defmodule Foo do
  defmacro __using__(variables) do
    quote do 
      def mapify(unquote(Enum.map(variables, &Macro.var(&1, nil))) do
         # will yield compiler warnings about unused variables
         %{}

         # invalid quoted expression
         unquote(Enum.map(variables, &{&1, Macro.var(&1, nil)}) |> Map.new)
      end
    end
  end
end

Использование:

defmodule X do
  use Foo, [:alpha, :bravo, :charlie]
end

Как я могу сгенерировать это?

defmodule X do
  def mapify(alpha, bravo, charlie) do
    %{alpha: alpha, bravo: bravo, charlie: charlie}
  end
end

Эликсир 1.7.3, Эрланг 21.0

РЕДАКТИРОВАТЬ:

Это сгенерируетфункционально правильная версия, но меня беспокоит, что Enum.zip запускается при каждом вызове ...

defmodule Foo do
  defmacro __using__(variables) do
    vars = Enum.map(variables, &Macro.var(&1, nil))
    result = quote do 
      def mapify(unquote(vars)) do
         Enum.zip(unquote(variables), unquote(vars)) |> Map.new
      end
    end

    IO.puts("Macro generated:\n#{result |> Macro.to_string}")
    result
  end
end

сгенерировано:

def(mapify([alpha, bravo, charlie])) do
  Enum.zip([:alpha, :bravo, :charlie], [alpha, bravo, charlie]) |> Map.new() 
end

РЕДАКТИРОВАТЬ:

Пойдем с этим, если кто-то не может понять исходный запрос:

defmodule Foo do
  defmacro __using__(variables) do
    vars = Enum.map(variables, &Macro.var(&1, nil))
    pairs = Enum.map(variables, &{&1, Macro.var(&1, nil)}) 
    result = quote do 
      def mapify(unquote(vars)) do
         unquote(pairs) |> Map.new
      end
    end

    IO.puts("Macro generated:\n#{result |> Macro.to_string}")
    result
  end
end

Сгенерированный макрос:

def(mapify([alpha, bravo, charlie])) do
  [alpha: alpha, bravo: bravo, charlie: charlie] |> Map.new()
end

1 Ответ

0 голосов
/ 13 октября 2018

Правильный ответ , предоставленный Хосе :

Карта - это не выражение в кавычках, это значение.Каждый раз, когда у вас есть значение, вы должны убедиться, что оно Macro.escape/1 так, что оно становится AST:

quote do
  def foo(unquote(args)) do
    [unquote(map_kw), unquote(Macro.escape(map))]
  end
end

Тем не менее, все, что вам нужно:

- unquote(pairs) |> Map.new
+ unquote(Macro.escape(pairs))

Мой оригинальный ответ, который все еще работает, потому что он строит AST карты вручную:

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

defmodule Foo do
  defmacro __using__(variables) do
    # allow sigil as input
    variables = Macro.expand(variables, __ENV__)

    vars = Enum.map(variables, &Macro.var(&1, nil))
    kw = for var <- variables, do: {var, Macro.var(var, nil)}
    # ⇓⇓⇓⇓ HERE ⇓⇓⇓⇓
    map = {:%{}, [], kw}

    result =
      quote do
        def mapify(unquote(vars)) do
          unquote(map)
        end
      end

    IO.puts("Macro generated:\n#{result |> Macro.to_string}")
    result
  end
end
defmodule Test, do: use Foo, ~w|alpha bravo charlie|a

Обратите внимание, что я исправил еще одну проблему (в самой первой строке), чтобы этот макрос принимал сигил для списка атомов.

Кроме того, вы можете использовать Kernel.SpecialForms.unquote_splicing/1 вместо unquote в def mapify(unquote_splicing(vars)), чтобы он принимал список аргументов.


Sidenote: Я сомневаюсь, что понимаю цель этого конкретного макроса.Может быть, сигил общего назначения подойдет вам лучше.Или просто придерживайтесь универсального синтаксиса Elixir, этого достаточно кратко.

Пожалуйста, прочитайте этот пост Andrea для плюсов и минусов (он содержит ссылку на репозиторий с ~m short maps sigilистория.)

...