【问题标题】:Right way to produce elixir struct in macro在宏中生成长生不老药结构的正确方法
【发布时间】:2015-12-29 17:27:54
【问题描述】:

我有以下设计模式:我有一个长生不老药模块,它响应增长/变化数量的functions/0,称为Defaults。我也有CustomConfig 模块,它基本上是结构,派生默认值并假设实例化如下:

%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 跳舞的情况下从 Map 声明 defstruct

【问题讨论】:

    标签: macros elixir


    【解决方案1】:

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

    1. :"#{k}" 可能只是 k,因为 k 已经是一个原子
    2. 您不需要Dict.keys/1,因为您可以在循环内的键上进行模式匹配
    3. 您不需要地图,因为您可以直接从 Enum.map(或从推导式)返回列表
    4. 您不需要宏,因为您可以将任何表达式传递给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 的好处之一是您可以write assertive code。如果您利用这一点,您将对自己的代码越来越有信心。

    【讨论】:

    • 谢谢,在键上使用模式匹配的提示就是它。老实说,我使用宏只是因为我喜欢宏是纯 AST 的概念;我也会删除它。由于我正在从 ruby​​ 迁移,我仍然无法以原生方式在 Elixir 中进行编码,但我做的越多,我就越喜欢它。
    • 很高兴您喜欢! :)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-03
    • 2016-02-01
    • 2019-05-20
    • 1970-01-01
    • 1970-01-01
    • 2010-09-16
    相关资源
    最近更新 更多