我正在尝试“模板化”一些冗余代码。我可以得到它来生成有关未使用变量的编译器警告,但是当我尝试使用那些相同的变量时,会出现“无效的带引号的表达式”。
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
Elixir 1.7.3,Erlang 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
答案 0 :(得分:1)
正确答案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
请注意,我已更正此宏的另一个问题(在第一行中),以接受原子列表标记。
此外,您可能希望使用unquote
中的Kernel.SpecialForms.unquote_splicing/1
而不是def mapify(unquote_splicing(vars))
来接受参数列表。
边注:我怀疑我是否理解此特定宏的用途。通用印章可能会更适合您的需求。或者,只要保持通用的Elixir语法就足够简洁了。
请阅读Andrea的文章,以了解正反两方面的内容(它包含指向回购的链接,其中包含~m
个简短的地图标记历史记录。)