Reputation: 349
Elixir seems like a great language. But what is/are the best convention(s) for declaring global constants?
I expect perhaps a module attribute may be the most appropriate solution?
Swapping the definitions for horizontal_line below fixes the compilation error.
defmodule ElixirTesting do
horizontal_line = String.duplicate("-", 80)
# def horizontal_line, do: String.duplicate("-", 80)
def test_global_constant do
IO.puts(horizontal_line)
IO.puts("Console Report Section Title")
IO.puts(horizontal_line)
true
end
end
Upvotes: 3
Views: 1362
Reputation: 4507
Here is the macro I use for contants.
defmacro define(name, value) do
quote do
defmacro unquote(name), do: unquote(value)
end
end
note that you will need to require the module with your constants since they are macros themselves.
The benefit of this approach is that your constants can be use in matches, even in guards.
Upvotes: 1
Reputation: 9568
You have to sort of put on a different brain when you think about things like "global constants" in Elixir. In other languages you might hear that "everything is an object" -- in Elixir, you might say "everything is a function".
One simple way to achieve a "global"-like "constant" is simply to define some functions inside a dedicated module, e.g.
defmodule Constants do
def pi, do: 3.14
def timeout, 5_000
# ... etc...
end
You can easily reference its values via calls like Constants.pi
or Constants.timeout
-- or you can polish access further via metaprogramming as others have suggested and create a macro.
As others have pointed out, module attributes are a useful way of introducing a shared value when the values are only needed inside a single module.
In many cases, relying on application configuration is a useful way of establishing a known baseline, so you can rely on the humble Application.get_env/3
or the more strict Application.fetch_env!/2
to retrieve "global"-ish values.
You might also find it useful to to define environment variables to populate your application config, similar to how other languages hydrate values, e.g. using a package like dotenvy
that lets you access environment variables.
Upvotes: 5
Reputation: 154
One approach would be to create an app level constants module and put all the global constants inside it as you described. In order to export these constants, We need to create a function for them. eg:
defmodule App.Constants do
@request_timeout 5000
def get_request_timeout do
@request_timeout
end
end
You could use metaprogramming to get rid of these extra functions.
defmodule Constants do
defmacro const(const_name, const_value) do
quote do
def unquote(const_name)(), do: unquote(const_value)
end
end
end
then...
defmodule App.Constants do
import Constants
const :request_timeout, 5000
end
App.Constants.request_timeout # you can access request timeout in your codebase like this.
Upvotes: 2
Reputation: 965
If the constant is only used within that module, a module attribute is a good option. If the constant needs to be used outside of the module, you can declare a function of the same name that simply returns the module attribute.
Upvotes: 1