我正在开发一个模块,在use
d时扩展其他模块的功能。问题是,有时我想在模块定义中编写一些自定义代码,用于提升模块功能:
所以看起来像:
defmodule Included do
defmacro __using__(_) do
quote unquote: false do
# a lot of code
model
|> unquote(@callback)
|> another_bunch_of_things_in_pipe
end
end
end
我的模块
defmodule Poor do
use Included
@callback fn(model) -> %{ model | poor: true } end
end
毫不奇怪,我有invalid quoted expression: #Function<0.100338 in file:web/models/user.ex>
。我发现Jose Valim提到不可能取消匿名功能。
那么,您能介绍一下在elixir中传递和调用自定义代码到macros的好方法吗?
答案 0 :(得分:3)
quote
正在Poor
模块的上下文中运行,因此您无需取消引用模块属性。但是,@callback
保留供内部使用,因此您需要使用其他名称:
defmodule Included do
defmacro __using__(_) do
quote do
@callback_fun.()
end
end
end
defmodule Poor do
@callback_fun fn -> IO.puts "hello from callback" end
use Included
end
您也可以将该函数作为参数传递给use
,我认为它更清晰,因为它直接显示了函数的使用位置:
defmodule Included do
defmacro __using__(opts) do
fun = Keyword.get(opts, :callback)
quote bind_quoted: [fun: fun] do
fun.()
end
end
end
defmodule Poor do
use Included, callback: fn ->
IO.puts "hello from callback"
end
end
答案 1 :(得分:1)
您的代码存在一些问题(除了遗漏的end
到fn
,这可能是一个错字):
@callback
是Elixir中的一个特殊属性。您可以阅读更多相关信息here。您必须使用其他名称。
为模块属性指定内容的语法为@attr value
,而不是@attr = value
。
您不需要unquote
该属性,您可以直接进入@attr.()
。
当您致电__using__
时调用use
,您需要在调用use
之前声明该属性。
这是固定版本:
defmodule Included do
defmacro __using__(_) do
quote do
model = %{poor: false}
model
|> @back.()
|> IO.inspect
end
end
end
defmodule Poor do
@back fn(model) -> %{ model | poor: true } end
use Included
end
输出:
%{poor: true}