我需要在@attr
A.target
的真实价值
defmodule A do
defmacro target(param) do
IO.inspect param, label: "Inside a macro"
end
end
defmodule B do
@attr 42
require A
A.target(@attr)
end
我当然有
Inside a macro: {:@, [line: 10], [{:attr, [line: 10], nil}]}
打印,因为此时AST正在构建,A.target/1
不负责扩展其参数。我看到我可以访问参数的值,而不是quote do
块中的AST:
defmodule A do
defmacro target(param) do
quote do: IO.inspect uquote(param), label: "unquote"
end
end
问题是我需要预先扩展价值。我尝试过:
var!
Macro.expand
Code.eval
所以,我的问题是:是否可以在宏体的第一行强制扩展宏参数?
答案 0 :(得分:1)
我不相信你可以。在宏的引用块之外,@ attr没有值。它只是一个引用的表达。模块B尚未编译。您只能在A的引用块内访问@attr,因为它在B。
的上下文中执行答案 1 :(得分:0)
有点迟到了这个问题;编写Elixir宏的新手。
我不建议这样编程,但这是一个有趣的练习!
您的问题似乎与使用常量@attr有关。
这是这个问题的主要痛点!
绝对是hack,但是您可以通过两个更改进行转换。
1)将元组从宏转换为列表。
2)宏中的attr表现不佳,因此您需要将其更改为str。
第二个特定问题是eval再次发生的错误,即使您在模块内调用它也可能会连接到某个宏模块。
** (ArgumentError) cannot invoke @/1 outside module
(elixir) lib/kernel.ex:5212: Kernel.assert_module_scope/3
(elixir) expanding macro: Kernel.@/1
nofile:11: (file)
expanding macro: A.target/1
try.exs:11: B (module)
通过一些巧妙的操作,可以操纵值以执行您想要的任何事情,只需确保在取消引用之前将列表转换回元组,然后将“ attr”转换为{attr}。
解决方案:
defmodule A do
def foo( {item1, item2, [ {item4, item5, item6} ] } ) do
[item1, item2, "#{item4}", item5, item6]
end
defmacro target(param) do
macroModified = param |> foo()
quote do
param = unquote(param)
macroModified = unquote(macroModified )
IO.inspect [result: param, macro: [macroModified ]]
end
end
end
defmodule B do
@attr 42
require A
A.target(@attr)
end
答案 2 :(得分:-1)
这不是你想要的吗?
defmodule A do
defmacro target(param) do
quote do
IO.inspect unquote(param), label: "Inside a macro"
end
end
end
defmodule B do
@attr 42
require A
A.target(@attr)
end
宏接收引用的参数。要获得实际值,您必须取消引用它。您只能在引用块内取消引用参数。
当宏返回带引号的表达式时,它会插入到调用模块中并运行,因此请知道IO.inspect...
模块中正在运行B
。