将关键字列表发送到宏并使用bind_quoted

时间:2016-06-29 03:06:24

标签: metaprogramming elixir

如何将关键字列表发送到宏并使用bind_quoted?以下是一个示例:

带宏的模块:

defmodule MacroTime do

  defmacro __using__(opts) do
    quote bind_quoted: [opts: opts] do

      def from_opts do
        # Using opts here produces an undefined function error
        IO.puts(opts[:foo])
      end

    end
  end

end

导入模块和脚本:

defmodule Main do
  use MacroTime, foo: "bar"
end

Main.from_opts

运行它会产生: ** (CompileError) main.ex:2: undefined function opts/0

您可以在此处试用:https://glot.io/snippets/eg2gg4huj3

我觉得我错过了一些关于宏的简单概念。

1 个答案:

答案 0 :(得分:1)

我认为这是因为您引用的地方(绑定)不正确。

这是一个类似的问题: Elixir macros and bind_quoted

这是Metaprogramming Elixir一书的定义:

  

bind_quoted

     

quote宏的bind_quoted选项将绑定传递给块,   确保外部绑定变量不加引号   时间。

让我们更清楚。看看这个例子

defmodule Print do

  defmacro __using__(opts) do
    IO.puts "In macro's context #{__MODULE__}"    # Macro context
    quote bind_quoted: [opts: opts] do
      IO.puts "In caller's context #{__MODULE__}" # Caller context
      IO.inspect opts

      def opts do
        IO.puts "In function definition's context"
      end
    end
  end

end

编译:

iex(1)> defmodule Test do
...(1)> use Print, foo: "bar"
...(1)> end
In macro's context Elixir.Print
In caller's context Elixir.Test
[foo: "bar"]
{:module, Test,
 <<70, 79, 82, 49, 0, 0, 5, 24, 66, 69, 65, 77, 69, 120, 68, 99, 0, 0, 0, 127,
   131, 104, 2, 100, 0, 14, 101, 108, 105, 120, 105, 114, 95, 100, 111, 99, 115,
   95, 118, 49, 108, 0, 0, 0, 4, 104, 2, ...>>, {:opts, 0}}
iex(2)> Test.opts
In function definition's context
:ok
iex(3)>

要想清楚注入代码,您必须使用unquote

您在这里做的是将绑定变量opts传递给引用块(来电者上下文),然后在内部调用它(函数定义的上下文

澄清上下文的定义。这来自书:

  

上下文是调用者绑定,导入和别名的范围。   对于宏的调用者来说,上下文是宝贵的。它符合您的观点   对于世界而言,凭借不变性,你不要指望你的   从你下面改变的变量,导入和别名。

最后您打算使用bind_quoted。我建议您阅读 Hygiene保护来电者的背景 Overriding Hygiene 以考虑另一种解决方案以符合您的目的

这是我到目前为止所想到的。希望有所帮助!