cppProgrammer
cppProgrammer

Reputation: 349

How can a global constant be declared in Elixir?

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

Answers (4)

Steve Pallen
Steve Pallen

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

Everett
Everett

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

rathourarv
rathourarv

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

Jan
Jan

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

Related Questions