我有以下设计模式:我有一个elixir模块,它响应增长/变化量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
虽然这很好用,但我很确定,应该有更直接/优雅/不太麻烦的方式来实现这个功能.
所以我的问题是:如何defstruct
在Map
没有跳舞的情况下宣布这个paso doble在map-reduce周围?
你的代码中有许多你真正不需要的间接.例子:
:"#{k}"
可能只是k
因为k
已经是一个原子
你不需要Dict.keys/1
因为你可以在循环内的键上进行模式匹配
您不需要地图,因为您可以直接从Enum.map(或从理解中)返回列表
您不需要宏,因为您可以传递任何表达式 defstruct
以下是如何重写代码:
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的一个好处是你可以编写自信代码.如果您利用它,您将对代码越来越有信心.