如何在 Elixir 中声明全局常量?

How can a global constant be declared in Elixir?

Elixir 似乎是一种很棒的语言。但是 is/are 声明全局常量的最佳约定是什么?

我希望模块属性可能是最合适的解决方案?

交换下面 horizontal_line 的定义可修复编译错误。

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

如果常量仅在该模块内使用,模块属性是一个不错的选择。如果常量需要在模块外使用,您可以声明一个同名函数,只需 returns 模块属性。

一种方法是创建一个应用程序级常量模块,并按照您的描述将所有全局常量放入其中。为了导出这些常量,我们需要为它们创建一个函数。例如:

defmodule App.Constants do
  @request_timeout 5000
  def get_request_timeout do
    @request_timeout
  end
end

您可以使用元编程来摆脱这些额外的功能。

defmodule Constants do
  defmacro const(const_name, const_value) do
    quote do
      def unquote(const_name)(), do: unquote(const_value)
    end
  end
end

然后...

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.

当您考虑 Elixir 中的“全局常量”之类的东西时,您必须换个脑子。在其他语言中,您可能会听到“一切都是对象”——在 Elixir 中,您可能会说“一切都是函数”。

实现类似“全局”的“常量”的一种简单方法是在专用模块中定义一些函数,例如

defmodule Constants do
  def pi, do: 3.14
  def timeout, 5_000
  # ... etc...
end

您可以通过 Constants.piConstants.timeout 之类的调用轻松引用它的值——或者您可以像其他人建议的那样通过元编程进一步完善访问并创建一个宏。

正如其他人指出的那样,当仅在单个模块内需要值时,模块属性是引入共享值的有用方法。

在许多情况下,依靠应用程序配置是建立已知基线的有用方法,因此您可以依靠不起眼的 Application.get_env/3 or the more strict Application.fetch_env!/2 来检索“全局”值。

您可能还会发现定义环境变量以填充您的应用程序配置很有用,类似于其他语言水化值的方式,例如使用像 dotenvy 这样的包可以让您访问环境变量。

这是我用于常量的宏。

  defmacro define(name, value) do
    quote do
      defmacro unquote(name), do: unquote(value)
    end
  end

请注意,您需要使用常量来要求模块,因为它们本身就是宏。

这种方法的好处是您的常量可以在比赛中使用,甚至可以在守卫中使用。