代码之家  ›  专栏  ›  技术社区  ›  Austin Salonen gmlacrosse

使用Macro.var创建的变量

  •  0
  • Austin Salonen gmlacrosse  · 技术社区  · 6 年前

    我在尝试“模板化”一些多余的代码。我可以让它生成关于未使用变量的编译器警告,但是如果我尝试使用那些相同的变量,就会得到“无效的引号表达式”。

    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 回复  |  直到 6 年前
        1
  •  2
  •   Aleksei Matiushkin    6 年前

    正确答案 provided by José :

    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)) 使其接受参数列表。


    旁注: 我怀疑我是否理解这个特殊宏的目的。也许通用的sigil更适合你的需要。或者,只使用一般的Elixir语法,它足够简洁。

    Andrea 对于赞成和反对(它包含一个链接到回购与 ~m 短地图(历史记录)