Macros 在Elixir宏中使用防护装置
我正在研究宏,它将采用一个函数并添加一些附加功能。例如: 这: 应转换为:Macros 在Elixir宏中使用防护装置,macros,elixir,Macros,Elixir,我正在研究宏,它将采用一个函数并添加一些附加功能。例如: 这: 应转换为: def this_works(a, b) do IO.puts("LOGGING whatever") a + b + 1 end 这就是我目前所拥有的。尝试在iex中运行这段代码: defmodule MyMacro do defmacro defstate(ast, do: block) do {fn_atom, _} = Macro.decompose_call(a
def this_works(a, b) do
IO.puts("LOGGING whatever")
a + b + 1
end
这就是我目前所拥有的。尝试在iex中运行这段代码:
defmodule MyMacro do
defmacro defstate(ast, do: block) do
{fn_atom, _} = Macro.decompose_call(ast)
quote do
def unquote(fn_atom)(var!(a), var!(b)) do
IO.puts("LOGGING")
unquote(block)
end
end
end
end
defmodule Test1 do
import MyMacro
defstate this_works(a, b) do
a + b + 1
end
end
Test.this_works(1, 2)
这正如预期的那样有效
现在,此模块不编译:
defmodule Test2 do
import MyMacro
defstate this_fails(a, b)
when 1 < 2
when 2 < 3
when 3 < 4 do
a + b + 1
end
end
唯一的变化是我添加了一个保护,宏无法处理这个问题
如何改进MyMacro.defstate,使其与具有任意数量保护的函数一起工作?如果您使用defstate this\u fails a,b当1<2时检查fn\u atom,您将看到它是:when而不是:this\u fails。这是因为表达式在Elixir AST中的表示方式:
iex(1)> quote do
...(1)> def foo, do: 1
...(1)> end
{:def, [context: Elixir, import: Kernel],
[{:foo, [context: Elixir], Elixir}, [do: 1]]}
iex(2)> quote do
...(2)> def foo when 1 < 2, do: 1
...(2)> end
{:def, [context: Elixir, import: Kernel],
[{:when, [context: Elixir],
[{:foo, [], Elixir}, {:<, [context: Elixir, import: Kernel], [1, 2]}]},
[do: 1]]}
我还更改了def之后的unquote,以确保保留when子句。如果使用defstate this\u fails a,b当1<2时检查fn\u atom,您将看到它是:when而不是:this\u fails。这是因为表达式在Elixir AST中的表示方式:
iex(1)> quote do
...(1)> def foo, do: 1
...(1)> end
{:def, [context: Elixir, import: Kernel],
[{:foo, [context: Elixir], Elixir}, [do: 1]]}
iex(2)> quote do
...(2)> def foo when 1 < 2, do: 1
...(2)> end
{:def, [context: Elixir, import: Kernel],
[{:when, [context: Elixir],
[{:foo, [], Elixir}, {:<, [context: Elixir, import: Kernel], [1, 2]}]},
[do: 1]]}
我还更改了def之后的unquote,以确保保留when子句。对defstate的调用在编译时从defmacro扩展到quote块中的内容。因此,保护表达式不会直接应用于宏调用,因为在编译时,内部定义的函数不会被调用
因此,您必须抓取:当您自己对元组进行分组并自己添加防护:
defmodule MyMacro do
defmacro defstate({:when, _, [ast, guards]}, do: block) do
{fn_atom, _} = Macro.decompose_call(ast)
quote do
def unquote(fn_atom)(var!(a), var!(b)) when unquote(guards) do
IO.puts("LOGGING")
unquote(block)
end
end
end
end
请注意我现在如何匹配{:when,{ast,guards]}元组
当使用保护调用宏时,它会将原始ast放在参数列表的第一项中,将保护表达式放在第二项中
请注意,如果要在不使用保护子句的情况下使用宏,您仍然必须在下面定义一个“捕获所有”宏定义。对defstate的调用在编译时从defsmacro扩展到quote块中的内容。因此,保护表达式不会直接应用于宏调用,因为在编译时,内部定义的函数不会被调用
因此,您必须抓取:当您自己对元组进行分组并自己添加防护:
defmodule MyMacro do
defmacro defstate({:when, _, [ast, guards]}, do: block) do
{fn_atom, _} = Macro.decompose_call(ast)
quote do
def unquote(fn_atom)(var!(a), var!(b)) when unquote(guards) do
IO.puts("LOGGING")
unquote(block)
end
end
end
end
请注意我现在如何匹配{:when,{ast,guards]}元组
当使用保护调用宏时,它会将原始ast放在参数列表的第一项中,将保护表达式放在第二项中
请注意,如果您想在不使用保护子句的情况下使用宏,您仍然必须在下面定义一个“全包”宏定义。您的答案更好:这几乎是好的。然而,我的问题是如何使这个宏与具有任意数量保护的函数一起工作:我可以对macro.decompose_callast的结果进行一些复杂的递归模式匹配。然而,我正在寻找更简单更容易的方法来实现这一点。你说的任何数量的警卫是什么意思?一个函数只能有一个防护装置。@为什么上面的方法对它很有效,你可以自己检查一下。第二个when是and的别名。是的!一切正常。谢谢你的答案更好:这几乎是好的。然而,我的问题是如何使这个宏与具有任意数量保护的函数一起工作:我可以对macro.decompose_callast的结果进行一些复杂的递归模式匹配。然而,我正在寻找更简单更容易的方法来实现这一点。你说的任何数量的警卫是什么意思?一个函数只能有一个防护装置。@为什么上面的方法对它很有效,你可以自己检查一下。第二个when是and的别名。是的!一切正常。谢谢
defmodule MyMacro do
defmacro defstate({:when, _, [ast, guards]}, do: block) do
{fn_atom, _} = Macro.decompose_call(ast)
quote do
def unquote(fn_atom)(var!(a), var!(b)) when unquote(guards) do
IO.puts("LOGGING")
unquote(block)
end
end
end
end