【问题标题】:elixir metaprogramming for module attributes模块属性的长生不老药元编程
【发布时间】:2018-10-30 11:16:53
【问题描述】:

我有这个模块

defmodule ElixirMeta.LangLoader do

  @external_resource [Path.join([__DIR__, "es.json"]),
                      Path.join([__DIR__, "en.json"])]

  defmacro __using__(_) do
    for lang <- ["es", "en"] do
      {:ok, body} = File.read(Path.join([__DIR__, "#{lang}.json"]))
      {:ok, json} = Poison.decode(body)
      quote do
        def lang(unquote(lang)), do: unquote(Macro.escape(json))
      end
    end
  end
end

defmodule ElixirMeta.Lang do
  use ElixirMeta.LangLoader
end

我知道我可以定义如下函数:

def lang(unquote(lang)), do: unquote(Macro.escape(json))

并且可以这样调用:

Lang.lang("es")

还要修改它的函数名,像这样:

def unquote(:"lang_#{lang}")(), do: unquote(Macro.escape(json))

然后这样称呼:

Lang.lang_es

但是是否可以对模块属性做同样的事情?

并且是编译的模块属性(?)我认为不可能从宏初始化它?也许我必须在before_compile 宏中执行此操作?

出于示例的目的,我想访问 Lang.lang_es 作为 @lang_es@lang_en LangLoader 属性

【问题讨论】:

    标签: macros elixir metaprogramming


    【解决方案1】:

    是的,可以使用Module.put_attribute/3 实现这一点(我已经根据您的初始代码创建了一个 MCVE):

    defmodule ElixirMeta.LangLoader do
      defmacro __using__(_) do
        [
          (quote do: Module.register_attribute __MODULE__,
            :langs, accumulate: true) |
          for lang <- ["es", "en"] do
            quote do
              def lang(unquote(lang)), do: unquote(lang)
              Module.put_attribute __MODULE__,
                :"lang_#{unquote(lang)}", unquote(lang)
              Module.put_attribute __MODULE__,
                :langs, unquote(lang)
            end
          end
        ]
      end
    end
    
    defmodule ElixirMeta.Lang do
      use ElixirMeta.LangLoader
    
      def test do
        IO.inspect {
          @lang_es,
          Enum.find(@langs, & &1 == "es"),
          lang("es")
        }, label: "Variants"
      end
    end
    
    ElixirMeta.Lang.test
    #⇒ Variants: {"es", "es", "es"}
    

    上面的代码声明了累积属性(@attr :foo 后跟@attr :bar 将产生[:foo, :bar] 值,而不是覆盖属性值、单个属性和函数。

    请注意,无法从外部访问模块属性,因为模块属性是编译时实体。

    【讨论】:

      猜你喜欢
      • 2020-03-29
      • 2016-02-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多