2016-06-08 66 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