在宏中生成elixir结构的正确方法

时间:2015-12-29 07:07:57

标签: macros elixir

我有以下设计模式:我有一个elixir模块,它响应{em>增长/更改金额functions/0,称为Defaults。我还有CustomConfig模块,它基本上是struct,派生默认值并假设实例化如下:

%CustomConfig{ foo: "bar" }

其中初始化属性被覆盖,其他属性取自Defaults,而Defaults中没有相同名称的函数则被拒绝。到目前为止,非常好。

要独立于内容(Defaults中的函数列表)实现此行为,我使用宏(在其他模块中,因为在结构声明中不能使用在同一模块中定义的宏):

defmacro define_struct_with_defaults do
  quote do
    defstruct Map.to_list(
      quote do: unquote(Enum.reduce(Dict.keys(
                        Defaults.__info__(:functions)), %{}, fn(k, acc) ->
        Map.put(acc, :"#{k}", apply(Defaults, :"#{k}", []))
      end)))
  end
end

虽然这很好用,但我很确定,应该有更直接/优雅/不太麻烦的方式来实现这个功能。

所以我的问题是:如何在没有在map-reduce 周围跳过这个paso doble的情况下从defstruct声明Map

1 个答案:

答案 0 :(得分:6)

你的代码中有许多你真正不需要的间接。例子:

  1. :"#{k}"可能只是k因为k已经是原子
  2. 您不需要Dict.keys/1,因为您可以在循环内的密钥上进行模式匹配
  3. 您不需要地图,因为您可以直接从Enum.map(或从理解中)返回列表
  4. 您不需要宏,因为您可以将任何表达式传递给defstruct
  5. 以下是重写代码的方法:

    defmodule Default do
      def foo, do: 1
      def bar, do: 2
    end
    
    defmodule Config do
      data =
        # Get all functions with 0 arity and the respective default
        for {k, 0} <- Default.__info__(:functions) do
          {k, apply(Default, k, [])}
        end
    
      defstruct data
    end
    

    Elixir的一个好处是你可以write assertive code。如果您利用它,您将对代码越来越有信心。