The @
symbol in Elixir denotes module attributes, which are useful compile-time settings. You often see them in places where you might put class constants in an OO language.
However, module attributes are more subtle than what you might find in an OO language. Here are some important takeaways:
They do not use the =
to assign a value (as you might be in the habit of doing if you are used to defining class constants in OO-land). The syntax is more like function input
where the optional parentheses are dropped.
Module attributes can be redefined multiple times throughout the module. You'll see this frequently with @doc
attributes that annotate the function that follows it, with @spec
which annotates the function input/output, or inside tests with @tag
to change the inputs to the test that follows it. This can offer a useful way to put big values out of the way of the function logic to improve readability.
Module attributes can be accumulated. Normally, each instance of an attribute reassigns its value, but if you set accumulate: true
when you register the attribute, then subsequent definitions will accumulate so that reading the attribute will return all the accumulated values. From the doc page:
defmodule MyModule do
Module.register_attribute(__MODULE__, :custom_threshold_for_lib, accumulate: true)
@custom_threshold_for_lib 10
@custom_threshold_for_lib 20
@custom_threshold_for_lib #=> [20, 10]
end
- Module attributes are evaluated at compile-time. Because they can raise visibility on important module-wide values, you might be tempted to do something like stash an ENV value:
defmodule Trouble do
@my_value System.fetch_env("BOOM") # <-- don't do this!
end
More recent versions of Elixir will show warnings if you attempt to do this (and some values, e.g. captured functions, will raise an error), so as a general rule of thumb, it's best to keep the module attributes simple and static.