Elixir macros and bind_quoted

时光总嘲笑我的痴心妄想 提交于 2019-12-09 11:30:02

问题


I have a macro that defines a module like so.

defmodule Bar do
  def bar do
    IO.puts "I am #{inspect __MODULE__}"
  end
end

defmodule MacroFun do

  defmacro define_module(name) do
    quote do
      defmodule unquote(name) do
        import Bar
        def foo do
          bar
          IO.puts "I am #{inspect __MODULE__}"
        end
      end
    end
  end

end

defmodule Runner do
  require MacroFun

  def run do
    MacroFun.define_module Foo
    Foo.foo
  end

end

Runner.run

The output of running this is:

I am Bar
I am Runner.Foo

Which makes sense; MacroFun.define_module was called in Runner.run so the module was defined and thus nested under the Runner module.

But now if I change MacroFun.define_module to use the :bind_quoted option:

  defmacro define_module(name) do
    quote bind_quoted: [name: name] do
      defmodule name do
        import Bar
        def foo do
          bar
          IO.puts "I am #{inspect __MODULE__}"
        end
      end
    end
  end

The output now becomes:

I am Bar
I am Foo

Why??


回答1:


I think that's because the place where you are unquoting (binding) the variable name.

In the first case, you are unquoting the variable name when creating a module, thus binding the variable at that moment would require to check for context (check if the code is inside another module, for example). So, you get your current atom plus the appropriate context: Runner.Foo.

In the second case, you are unquoting the variable name before it's placed in a context, therefore its value will not change and it'll be the atom Foo (no context).




回答2:


With this code you will see the correct values used to create the modules:

require Logger

defmodule Bar do
  def bar do
    IO.puts "I am #{inspect __MODULE__}"
  end
end

defmodule MacroFun do

  defmacro define_module(name) do
    quote do
      Logger.debug("#{inspect unquote(name)}")
      defmodule unquote(name) do
        import Bar
        Logger.debug("#{inspect unquote(name)}")
        def foo do
          bar
          IO.puts "I am #{inspect __MODULE__}"
        end
      end
    end
  end

  defmacro define_module2(name) do
    quote bind_quoted: [name: name] do
      defmodule name do
        import Bar
        Logger.debug("#{inspect name}")
        def foo do
          bar
          IO.puts "I am #{inspect __MODULE__}"
        end
      end
    end
  end
end

defmodule Runner do
  require MacroFun

  def run do
    MacroFun.define_module Foo
    Foo.foo
  end
  def run2 do
    MacroFun.define_module2 Foo2
    Foo2.foo
  end

end

Runner.run
Runner.run2

Output:

[warn]  Foo
[warn]  Runner.Foo
I am Bar
I am Runner.Foo

[warn]  Foo2
I am Bar
I am Foo2


来源:https://stackoverflow.com/questions/34889369/elixir-macros-and-bind-quoted

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!