访问模块外部的模块属性

时间:2016-06-08 21:28:05

标签: elixir

我在elixir中有一个带有属性的模块:

defmodule MyAwesomeModule do
  @awesome_number 7

  # other stuff...
end

我无法访问模块外的@awesome_number。我已尝试使用Module.get_attribute/2方法,但它会抛出此错误:

iex(79)> Module.get_attribute(MyAwesomeModule, :awesome_number)
** (ArgumentError) could not call get_attribute on module MyAwesomeModule because it was already compiled
    (elixir) lib/module.ex:1101: Module.assert_not_compiled!/2
    (elixir) lib/module.ex:1016: Module.get_attribute/3

所以现在,我将模块属性包装在一个访问它的方法中,但它对我来说并没有多大意义。我可以简单地使用该方法并停止一起使用该属性:

defmodule MyAwesomeModule do
  @awesome_number 7

  def awesome_number, do: @awesome_number

  # other stuff...
end

所以我的问题是,有更好/正确的方法吗?

2 个答案:

答案 0 :(得分:18)

AFAIK无法访问给定模块之外的模块属性。定义一个公开模块属性的函数是要走的路,就是你正在做的事情。

仍然有充分的理由保留模块属性,而不是仅使用没有模块属性的函数。这取决于具体情况。请记住,存储在模块属性中的值是在编译时计算的。话虽如此,您可能有不同的理由使用不使用模块属性。让我们看看以下示例:

如果awesome_number每次访问时都必须随机生成,则必须使用函数。

如果awesome_number需要计算(长时间)并且不必更改其值,那么使用模块属性+函数来公开它,是可行的方法。

修改

我之前说过的模块属性还有很多。它们的功能优于功能。以下是elixir文档中的示例和引用:

defmodule MyServer do
  @my_data 14
  def first_data, do: @my_data
  @my_data 13
  def second_data, do: @my_data
end

MyServer.first_data #=> 14
MyServer.second_data #=> 13
  

请注意,读取函数内的属性会拍摄快照   它的当前价值。换句话说,在编译时读取该值   时间,而不是在运行时。正如我们将要看到的,这会产生属性   在模块编译期间用作存储空间很有用。

将它们与Module.register_attribute/3https://hexdocs.pm/elixir/Module.html#register_attribute/3)一起使用,尤其是accumulate: true选项,可以在更多方面使用它们。

我想说的是,它们比仅仅用作常量更有用。

答案 1 :(得分:8)

使用use和宏有一种“作弊”的方法。请看this example

例如,假设您将模块定义为:

defmodule AwesomeLibrary do  
  defmacro __using__(_) do
    quote do
      def print(s), do: IO.puts(s)
    end
  end
end 

然后,在某些模块中,您可以通过这种方式使用use关键字。

defmodule TestLibrary do  
  use AwesomeLibrary
end

效果是__using__块中定义的所有内容都在编译时复制到新模块中。因此,在这种情况下,即使在另一个模块中定义了TestLibrary.print,也可以使用print

这也用于复制常量。作为示例,您可以查看TimeX库。它使用在需要时导入的a dedicated module for constants

在我看来,这是在大型代码库中共享常量定义的更好方法。