2016-06-08 72 views
6

我有灵药该模块与属性外:接入模块属性模块

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 

所以我的问题是,是有这样做的更好/有道?

回答

12

AFAIK无法访问给定模块之外的模块属性。定义一个暴露模块属性的函数是一种行之有效的方法,就是你已经在做的事情。

仍然有一个很好的理由保持模块属性,而不是只使用没有模块属性的函数。这取决于上下文。请记住,模块属性中存储的值是在编译时计算的。这就是说你可以有不同的理由使用不使用模块属性。我们来看下面的例子:

如果awesome_number必须在每次访问时随机生成,那么您必须使用函数。

如果需要计算awesome_number(长时间)并且不需要更改其值,那么使用模块属性+函数来显示它,就是要走的路。

编辑:

还有更多的模块从我前面说的属性。他们的表现比功能更好。下面是一个例子,从灵药文档报价:与Module.register_attribute/3http://elixir-lang.org/docs/stable/elixir/Module.html#register_attribute/3),特别是与accumulate: true选项

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 

Notice that reading an attribute inside a function takes a snapshot of its current value. In other words, the value is read at compilation time and not at runtime. As we are going to see, this makes attributes useful to be used as storage during module compilation.

使用它们,使它们在很多方面非常有用。

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

+0

嗯。海事组织似乎是一个非常薄弱的​​原因。我没有看到其他使用模块属性的其他用法(除了文档,标签等...) – Sheharyar

+1

Elixir的Plug库是一个很好的使用“accumulate:true”模块属性的例子。如果您想在源代码中挖掘更多内容,请点击此链接https://github.com/elixir-lang/plug/blob/19f53a67e672152a7393611681431c1e0ec1be04/lib/plug/builder.ex#L121 – ventsislaf

4

通过使用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__块中定义的一切在编译时新模块中被复制。所以,在这种情况下,即使在另一个模块中定义了print,也可以使用TestLibrary.print

这也用于复制常量。作为一个例子,你可以看看TimeX库。它使用a dedicated module for constants,只要需要,它就会被导入。

在我看来,这似乎是一个更好的方式来分享一个大代码库的常量定义。

+1

这是TimeX中的绝佳链接。这也是我使用它们的方式。我创建了一个概念应用程序,以查看跨项目共享“常量”的不同方式:https://github.com/bill-mybiz/elixir-constants-concept。我还在常量上创建了一个SO“文档”(现在正在审查中),因为这是elixir文档不那么强大的少数几个领域之一。 (几乎所有其他内容都精彩地记录在案)。这种方法的另一个好处是你可以在guard语句中使用它们(因为它是编译时)。 – ibgib