使用模块作为常量

Using modules as constants

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

我意识到我可以为我的密钥使用 Elixir 模块(实际上只是原子),而不是常规原子。不过我不确定这是否形式不佳。

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

因为它们“只是原子”,我不认为这样做会导致比其他方式更多的性能损失?同时,通过常规原子似乎对 elixir 更自然一些?

我不太担心将错误的原子插入数据库,因为我使用这两种方法检查 cast/dump,这更像是一个风格问题。

此处的最佳做法是什么?你会怎么做?

模块作为键​​

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,
  # ...
}

没有灵丹妙药。我经常使用 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,
  # ...
}