Another gotcha with module attributes is that each reference injects the value, which can increase disk & mem usage. For example:
defmodule Foo do
@x Enum.to_list(1..1_000_000)
def foo, do: @x
def bar, do: @x
end
Here we inject two identical large lists in the code, and the resulting file is 6 MB.
In contrast, the following code:
defmodule Foo do
def foo, do: x()
def bar, do: x()
defp x, do: unquote(Enum.to_list(1..1_000_000))
end
produces a 4 MB beam, because the list is injected into the compiled beam only once.
In addition, I consider the 2nd version more flexible, because the “constant” can be provided after the implementation, which I often find better than listing various constants at the top of the file.
It’s also worth mentioning that both versions produce constants, as in terms which are handled in a special way by the runtime. See here for details.