使用模块作为常量

时间:2018-11-19 01:43:01

标签: elixir ecto

我正在拧紧自定义的Ecto类型,其行为类似于枚举类型。我有一个原子列表关键字=>整数。

我意识到我可以将Elixir模块(实际上只是原子)用作我的键,而不是常规原子。我不确定这是不是很差的形式。

对我来说,使用模块1)看起来更重要,而且很明显可接受的值在范围上受到限制,而模块2)为正确的代码建议提供了一些机会。

由于它们只是“原子”,因此我不认为这样做会带来比我更高的性能损失吗?同时,通过常规原子似乎对长生不老药更自然?

我不太担心将错误的原子插入数据库中,因为我使用这两种方法检查了强制转换/转储,这更多地是一个风格问题。

这里的最佳做法是什么?你会怎么做?

模块密钥

defmodule App.Background.BackgroundJob.Pool do
  # you could probably macro this if you hated the look of the syntax
  defmodule Local do end
  defmodule Remote do end

  @behaviour Ecto.Type
  @pools [{Local, 0}, {Remote, 1}]

  # cast, load, etc...
end

job  = %{
  pool: BackgroundJob.Pool.Local,
  # ...
}

原子键

defmodule App.Background.BackgroundJob.Pool do

  @behaviour Ecto.Type
  @pools [{:local, 0}, {:remote, 1}]

  # possibly have these functions 
  def local, do: :local
  def remote, do: :remote

  # cast, load, etc...
end

job  = %{
  pool: BackgroundJob.Pool.local,
  # ...
}

# or

job  = %{
  pool: :local,
  # ...
}

1 个答案:

答案 0 :(得分:2)

没有银弹。我经常使用elixir模块作为键​​,但是只有在将它们作为模块有意义时(附加功能,功能等等)。

在这里使用模块只是为了保存几次按键。在我看来,它具有误导性和干扰性。为了避免代码重复和通过DRY,只需将您的模块转换为结构即可:

defmodule App.Background.BackgroundJob.Pool do
  @enum [local: 0, remote: 1]
  defstruct @enum

  @behaviour Ecto.Type
  @pools @enum

  # cast, load, etc...
end

job  = %{
  pool: BackgroundJob.Pool.local,
  # ...
}

或任何其他提供更多清晰度的方式,例如宏:

defmodule Enum do
  defmacro __using__(enum) do
    enum
    |> Enum.with_index()
    |> Enum.map(fn {name, idx} ->
      quote location: :keep do
        def unquote(name)(), do: unquote(idx)
      end
    end)
  end
end

use Enum, [:local, :remote]
job  = %{
  pool: BackgroundJob.Pool.local,
  # ...
}