檢視原始碼 模組 行為 (Elixir v1.16.2)
提供在編譯期間處理模組的函式。
它允許開發人員動態新增、刪除和註冊屬性、附加文件等。
在編譯模組後,使用此模組中的許多函式會引發錯誤,因為它們超出檢查執行時間資料的範圍。大多數執行時間資料可透過附加到每個編譯模組的 __info__/1
函式進行檢查。
模組屬性
每個模組都可以裝飾一個或多個屬性。Elixir 目前定義以下屬性
@after_compile
在當前模組編譯後立即呼叫的掛鉤。接受模組或 {module, function_name}
。請參閱下方的「編譯回呼」區段。
@after_verify
(自 v1.14.0 起)
在當前模組驗證未定義函式、不建議使用等問題後立即呼叫的掛鉤。接受模組或 {module, function_name}
。請參閱下方的「編譯回呼」區段。
@before_compile
在編譯模組前呼叫的掛鉤。接受模組或 {module, function_or_macro_name}
元組。請參閱下方的「編譯回呼」區段。
@behaviour
請注意英國拼寫法!
模組可以參照行為,以確保它們實作由 @callback
定義的特定函式簽章。
例如,你可以指定 URI.Parser
行為如下
defmodule URI.Parser do
@doc "Defines a default port"
@callback default_port() :: integer
@doc "Parses the given URL"
@callback parse(uri_info :: URI.t()) :: URI.t()
end
然後模組可以將其用作
defmodule URI.HTTP do
@behaviour URI.Parser
def default_port(), do: 80
def parse(info), do: info
end
如果行為變更或 URI.HTTP
未實作其中一個回呼,系統會提出警告。
如需詳細文件,請參閱 行為類型規格文件。
@impl
(自 v1.5.0 起)
為了協助正確實作行為,你可以選擇為行為的實作回呼宣告 @impl
。這會讓回呼明確化,並能協助你找出程式碼中的錯誤。編譯器會在以下情況中提出警告
如果你在函式不是回呼時標記
@impl
。如果你在其他函式標記
@impl
時,沒有在函式上標記@impl
。如果你在一個函式上標記@impl
,你必須將該行為的其他所有回呼標記為@impl
。
@impl
以每個內容為基礎運作。如果你透過巨集產生函式並標記 @impl
,這不會影響產生該函式的模組。
@impl
透過讓其他開發人員清楚函式正在實作回呼,也有助於維護性。
使用 @impl
,上述範例可以改寫為
defmodule URI.HTTP do
@behaviour URI.Parser
@impl true
def default_port(), do: 80
@impl true
def parse(info), do: info
end
你可以將 false
、true
或特定行為傳遞給 @impl
。
defmodule Foo do
@behaviour Bar
@behaviour Baz
# Will warn if neither Bar nor Baz specify a callback named bar/0.
@impl true
def bar(), do: :ok
# Will warn if Baz does not specify a callback named baz/0.
@impl Baz
def baz(), do: :ok
end
程式碼現在更具可讀性,因為現在清楚哪些函式是 API 的一部分,哪些是回呼實作。為了加強這個概念,@impl true
會自動將函式標記為 @doc false
,除非明確設定 @doc
,否則會停用文件。
@compile
定義模組編譯的選項。這用於設定 Elixir 和 Erlang 編譯器,以及外部工具新增的任何其他編譯傳遞。例如
defmodule MyModule do
@compile {:inline, my_fun: 1}
def my_fun(arg) do
to_string(arg)
end
end
多次使用 @compile
會累積,而不是覆寫先前的使用。請參閱下方的「編譯選項」區段。
@deprecated
(自 v1.6.0 起)
提供函式的棄用原因。例如
defmodule Keyword do
@deprecated "Use Kernel.length/1 instead"
def size(keyword) do
length(keyword)
end
end
Mix 編譯器會自動尋找呼叫已棄用的模組,並在編譯期間發出警告。
使用 @deprecated
屬性也會反映在給定函式和巨集的說明文件中。您可以在 @deprecated
屬性和說明文件元資料之間進行選擇,以提供硬棄用(帶有警告)和軟棄用(不帶警告)
這是一個軟棄用,因為它只是將說明文件註解為已棄用
@doc deprecated: "Use Kernel.length/1 instead"
def size(keyword)
這是一個硬棄用,因為它會發出警告並將說明文件註解為已棄用
@deprecated "Use Kernel.length/1 instead"
def size(keyword)
目前 @deprecated
僅支援函式和巨集。但是,您可以在註解元資料中使用 :deprecated
鍵來註解模組、類型和回呼的說明文件。
我們建議謹慎使用此功能,尤其是函式庫作者。棄用程式碼總是會將負擔推向函式庫使用者。我們還建議在棄用後長時間維護已棄用的功能,讓開發人員有充裕的時間進行更新(除非在某些情況下,例如存在安全性問題時,不希望保留已棄用的 API)。
@doc
和 @typedoc
提供後接屬性的實體文件。 @doc
用於函式、巨集、回呼或巨集回呼,而 @typedoc
用於類型(公開或不透明)。
接受其中之一
- 字串(通常是 heredoc)
false
,這將使實體對ExDoc
等文件提取工具不可見- 關鍵字清單,自 Elixir 1.7.0 起
例如
defmodule MyModule do
@typedoc "This type"
@typedoc since: "1.1.0"
@type t :: term
@doc "Hello world"
@doc since: "1.1.0"
def hello do
"world"
end
@doc """
Sums `a` to `b`.
"""
def sum(a, b) do
a + b
end
end
如上例所示,自 Elixir 1.7.0 起,@doc
和 @typedoc
也接受關鍵字清單,作為提供有關實體的任意元資料的方法。 ExDoc
和 IEx
等工具可以使用此資訊顯示註解。常見的用例是 :since
鍵,可用於註解函式引入的版本。
如範例所示,可以在實體之前多次使用這些屬性。但是,如果使用二進位檔兩次,編譯器會發出警告,因為這會取代前一次使用的文件文字。使用關鍵字清單多次使用會將清單合併為一個。
請注意,由於編譯器也定義了一些額外的元資料,因此有一些保留的鍵會被忽略,如果使用的話會發出警告。目前這些是::opaque
和 :defaults
。
一旦編譯這個模組,這些資訊就會透過 Code.fetch_docs/1
函數取得。
@dialyzer
定義在使用 :dialyzer
時請求或抑制的警告。
接受原子、元組或原子和元組的清單。例如
defmodule MyModule do
@dialyzer {:nowarn_function, [my_fun: 1]}
def my_fun(arg) do
M.not_a_function(arg)
end
end
有關支援的警告清單,請參閱 :dialyzer
模組。
重複使用 @dialyzer
會累積,而不是覆寫先前的使用。
@external_resource
指定當前模組的外部資源。
有時模組會內嵌來自外部檔案的資訊。這個屬性允許模組註解已使用的外部資源。
工具可以使用這些資訊來確保在任何外部資源變更時重新編譯模組,例如:mix compile.elixir
。
指定的檔案路徑被解釋為相對於包含專案的 mix.exs
的資料夾,也就是目前的作業目錄,而不是宣告 @external_resource
的檔案。
如果外部資源不存在,模組仍然依賴於它,導致在新增檔案後重新編譯模組。
@file
變更屬性後函數或巨集在堆疊追蹤中使用的檔名,例如
defmodule MyModule do
@doc "Hello world"
@file "hello.ex"
def hello do
"world"
end
end
請注意,這僅適用於來自定義內部範圍(包括其模式和防護)的例外/診斷。例如
defmodule MyModule do # <---- module definition
@file "hello.ex"
defp unused(a) do # <---- function definition
"world" # <---- function scope
end
@file "bye.ex"
def unused(_), do: true
end
如果您執行此程式碼,並將第二個「未使用的」定義註解掉,您會看到 hello.ex
在報告警告時會被用作堆疊追蹤,但如果您取消註解,您會看到錯誤不會提到 bye.ex
,因為它是一個模組層級錯誤,而不是表達式層級錯誤。
@moduledoc
提供目前模組的說明文件。
defmodule MyModule do
@moduledoc """
A very useful module.
"""
@moduledoc authors: ["Alice", "Bob"]
end
接受字串(通常是 heredoc)或 false
,其中 @moduledoc false
會讓模組對文件提取工具(例如 ExDoc
)不可見。
與 @doc
類似,也接受關鍵字清單來提供模組的元資料。有關更多詳細資訊,請參閱上方 @doc
的說明文件。
一旦編譯這個模組,這些資訊就會透過 Code.fetch_docs/1
函數取得。
@nifs
(自 v1.16.0 起)
將被原生實作(NIF)覆寫的函式清單及其元數。
defmodule MyLibrary.MyModule do
@nifs [foo: 1, bar: 2]
def foo(arg1), do: :erlang.nif_error(:not_loaded)
def bar(arg1, arg2), do: :erlang.nif_error(:not_loaded)
end
有關更多資訊,請參閱 Erlang 說明文件:https://erlang.dev.org.tw/doc/man/erl_nif
@on_definition
當目前模組中的每個函式或巨集被定義時,將會呼叫的掛鉤。在註解函式時很有用。
接受模組或 {module, function_name}
組合。函式必須採用 6 個參數
- 模組環境
- 函式/巨集的種類:
:def
、:defp
、:defmacro
或:defmacrop
- 函式/巨集名稱
- 引用的參數清單
- 引用的防護清單
- 引用的函式主體
如果要定義的函式/巨集有多個子句,則會針對每個子句呼叫掛鉤。
與其他掛鉤不同,@on_definition
只會呼叫函式,而不會呼叫巨集。這是為了避免 @on_definition
回呼重新定義剛剛定義的函式,而採用更明確的方法。
當只提供模組時,函式假設為 __on_definition__/6
。
範例
defmodule Hooks do
def on_def(_env, kind, name, args, guards, body) do
IO.puts("Defining #{kind} named #{name} with args:")
IO.inspect(args)
IO.puts("and guards")
IO.inspect(guards)
IO.puts("and body")
IO.puts(Macro.to_string(body))
end
end
defmodule MyModule do
@on_definition {Hooks, :on_def}
def hello(arg) when is_binary(arg) or is_list(arg) do
"Hello" <> to_string(arg)
end
def hello(_) do
:ok
end
end
@on_load
只要載入模組,就會呼叫的掛鉤。
接受當前模組中函數的函數名稱(作為原子)。該函數必須具有 0 元性(無參數)。如果函數未傳回 :ok
,則模組的載入將會中止。例如
defmodule MyModule do
@on_load :load_check
def load_check do
if some_condition() do
:ok
else
:abort
end
end
def some_condition do
false
end
end
@vsn
指定模組版本。接受任何有效的 Elixir 值,例如
defmodule MyModule do
@vsn "1.0"
end
結構屬性
@derive
- 為當前模組中定義的結構衍生給定協定的實作@enforce_keys
- 確保在建立當前模組中定義的結構時,總是設定給定的金鑰
請參閱 defstruct/1
以取得有關建立和使用結構的更多資訊。
類型規格屬性
下列屬性是類型規格的一部分,且也內建於 Elixir 中
@type
- 定義要在@spec
中使用的類型@typep
- 定義要在@spec
中使用的私人類型@opaque
- 定義要在@spec
中使用的不透明類型@spec
- 提供函數的規格@callback
- 提供行為回呼的規格@macrocallback
- 提供巨集行為回呼的規格@optional_callbacks
- 指定哪些行為回呼和巨集行為回呼是選用的@impl
- 宣告回呼函數或巨集的實作
如需詳細文件,請參閱 類型規格文件。
自訂屬性
除了上述內建屬性之外,也可以新增自訂屬性。自訂屬性使用 @/1
算子,後接有效的變數名稱來表示。提供給自訂屬性的值必須是有效的 Elixir 值
defmodule MyModule do
@custom_attr [some: "stuff"]
end
如需在定義自訂屬性時可用的更進階選項,請參閱 register_attribute/3
。
編譯回呼
有三個編譯回呼,依序呼叫:@before_compile
、@after_compile
和 @after_verify
。它們的說明如下。
@before_compile
在編譯模組之前會呼叫的掛勾。這通常用於變更當前模組的編譯方式。
接受模組或 {module, function_or_macro_name}
元組。函式/巨集必須帶有一個參數:模組環境。如果它是巨集,其傳回值將在編譯開始前注入到模組定義的結尾。
當僅提供模組時,函式/巨集假設為 __before_compile__/1
。
呼叫回會按其註冊順序執行。任何可覆寫的定義將在第一個呼叫回執行前具體化。定義可以在另一個編譯前呼叫回中再次變為可覆寫,並在所有呼叫回執行後最後一次具體化。
注意:呼叫回函式/巨集必須置於獨立模組中(因為呼叫呼叫回時,目前模組尚未存在)。
範例
defmodule A do
defmacro __before_compile__(_env) do
quote do
def hello, do: "world"
end
end
end
defmodule B do
@before_compile A
end
B.hello()
#=> "world"
@after_compile
在當前模組編譯後立即呼叫的掛鉤。
接受模組或 {module, function_name}
元組。函式必須帶有兩個參數:模組環境及其位元組碼。當僅提供模組時,函式假設為 __after_compile__/2
。
呼叫回會按其註冊順序執行。
Module
函式預期尚未編譯的模組(例如 definitions_in/1
)在呼叫 @after_compile
時仍可用。
範例
defmodule MyModule do
@after_compile __MODULE__
def __after_compile__(env, _bytecode) do
IO.inspect(env)
end
end
@after_verify
在當前模組針對未定義函式、不建議使用等進行驗證後立即呼叫的掛鉤。模組在編譯後總是會驗證。在 Mix 專案中,當任何執行時期依賴項變更時,模組也會驗證。因此,這對於在避免編譯時期依賴項的同時執行當前模組的驗證很有用。
接受一個模組或一個 {module, function_name}
元組。函式必須帶一個參數:模組名稱。當只提供一個模組時,函式假設為 __after_verify__/1
。
呼叫回會按其註冊順序執行。
Module
函式預期尚未編譯的模組在呼叫 @after_verify
時不再可用。
範例
defmodule MyModule do
@after_verify __MODULE__
def __after_verify__(module) do
IO.inspect(module)
:ok
end
end
編譯選項
@compile
屬性接受不同的選項,這些選項由 Elixir 和 Erlang 編譯器使用。以下文件記錄了一些常見的用例
@compile :debug_info
- 包含:debug_info
,無論Code.get_compiler_option/1
中對應的設定為何@compile {:debug_info, false}
- 停用:debug_info
,無論Code.get_compiler_option/1
中對應的設定為何。請注意,不建議停用:debug_info
,因為這會移除 Elixir 編譯器和其他工具靜態分析程式碼的能力。如果您想在部署時移除:debug_info
,像mix release
這樣的工具預設就會這麼做。@compile {:inline, some_fun: 2, other_fun: 3}
- 內聯給定的名稱/元組對。內聯會在區域內套用,來自其他模組的呼叫不會受到此選項影響@compile {:autoload, false}
- 停用編譯後自動載入模組。相反地,模組會在傳送給它之後載入@compile {:no_warn_undefined, Mod}
或@compile {:no_warn_undefined, {Mod, fun, arity}}
- 如果給定的模組或給定的Mod.fun/arity
未定義,則不會發出警告
摘要
回呼函式
提供模組定義的函式、巨集和其他資訊的執行時間資訊。
函式
傳回 module
中定義的所有模組屬性名稱。
串接別名的清單並傳回新的別名。
串接兩個別名並傳回新的別名。
使用指定的引號表示式建立一個模組,並指定名稱。
檢查模組是否定義指定的函式或巨集。
檢查模組是否定義指定 kind
的函式或巨集。
檢查目前的模組是否定義指定的類型(私有、不透明或其他)。
傳回在 module
中定義的所有函式和巨集。
傳回在 module
中定義的所有函式,依據其種類。
刪除指定模組屬性的項目(或項目)。
從模組中刪除定義。
在指定的模組內容中評估引號內容。
從模組中取得指定的屬性。
傳回指定名稱-元組對應的定義。
從模組中取得指定屬性的最後設定值。
檢查指定的屬性是否已定義。
讓 module
中指定的函式可被覆寫。
檢查模組是否已開啟。
如果 module
中的 tuple
已在某個時間點標示為可覆寫,則傳回 true
。
傳回 module
中所有可覆寫的定義。
在指定的 module
中放置一個具有 key
和 value
的模組屬性。
傳回 Elixir 使用的模組屬性資訊。
串接一個別名清單,並僅在別名已被參照時傳回新的別名。
串接兩個別名,並僅在別名已被參照時傳回新的別名。
將指定的規範複製為一個回呼函式。
將指定的模組名稱拆分為二進位元組。
類型
回呼
@callback __info__(:attributes) :: keyword()
@callback __info__(:compile) :: [term()]
@callback __info__(:functions) :: keyword()
@callback __info__(:macros) :: keyword()
@callback __info__(:md5) :: binary()
@callback __info__(:module) :: module()
@callback __info__(:struct) :: [%{field: atom(), required: boolean()}] | nil
提供模組定義的函式、巨集和其他資訊的執行時間資訊。
當模組編譯時,每個模組都會取得一個 __info__/1
函數。此函數會採用下列其中一項
:attributes
- 含有所有持續屬性的關鍵字清單:compile
- 含有編譯器中繼資料的清單:functions
- 公用函數及其元數的關鍵字清單:macros
- 公用巨集及其元數的關鍵字清單:md5
- 模組的 MD5:module
- 模組原子名稱:struct
- (自 v1.14.0 起) 如果模組定義結構,則依序為每個欄位
函數
傳回 module
中定義的所有模組屬性名稱。
此函數只能用於尚未編譯的模組。
範例
defmodule Example do
@foo 1
Module.register_attribute(__MODULE__, :bar, accumulate: true)
:foo in Module.attributes_in(__MODULE__)
#=> true
:bar in Module.attributes_in(__MODULE__)
#=> true
end
串接別名的清單並傳回新的別名。
它處理二進位和原子。
範例
iex> Module.concat([Foo, Bar])
Foo.Bar
iex> Module.concat([Foo, "Bar"])
Foo.Bar
串接兩個別名並傳回新的別名。
它處理二進位和原子。
範例
iex> Module.concat(Foo, Bar)
Foo.Bar
iex> Module.concat(Foo, "Bar")
Foo.Bar
@spec create(module(), Macro.t(), Macro.Env.t() | keyword()) :: {:module, module(), binary(), term()}
使用指定的引號表示式建立一個模組,並指定名稱。
定義模組的行及其檔案必須作為選項傳遞。
它傳回形狀為 {:module, module, binary, term}
的元組,其中 module
是模組名稱,binary
是模組位元組碼,term
是 quoted
中最後一個表達式的結果。
類似於 Kernel.defmodule/2
,只有在 Module.create/3
在目前正在編譯的檔案中呼叫時,位元組碼才會以 .beam
檔案寫入磁碟。
範例
contents =
quote do
def world, do: true
end
Module.create(Hello, contents, Macro.Env.location(__ENV__))
Hello.world()
#=> true
與 defmodule
的差異
Module.create/3
的運作方式類似於 Kernel.defmodule/2
,並傳回相同的結果。雖然也可以使用 Kernel.defmodule/2
來動態定義模組,但當模組主體由引號表達式提供時,建議使用此函數。
另一個重要的區別是,Module.create/3
允許您控制在定義模組時使用的環境變數,而 Kernel.defmodule/2
會自動使用它被呼叫時的環境。
@spec defines?(module(), definition()) :: boolean()
檢查模組是否定義指定的函式或巨集。
使用 defines?/3
斷言特定類型。
此函數只能用於尚未編譯的模組。使用 Kernel.function_exported?/3
和 Kernel.macro_exported?/3
分別檢查編譯模組中的公開函數和巨集。
請注意,對於已定義但標記為可覆寫且未提供其他實作的函數和巨集,defines?
會傳回 false
。您可以透過呼叫 overridable?/2
檢查可覆寫狀態。
範例
defmodule Example do
Module.defines?(__MODULE__, {:version, 0}) #=> false
def version, do: 1
Module.defines?(__MODULE__, {:version, 0}) #=> true
end
@spec defines?(module(), definition(), def_kind()) :: boolean()
檢查模組是否定義指定 kind
的函式或巨集。
kind
可以是 :def
、:defp
、:defmacro
或 :defmacrop
中的任何一個。
此函數只能用於尚未編譯的模組。使用 Kernel.function_exported?/3
和 Kernel.macro_exported?/3
分別檢查編譯模組中的公開函數和巨集。
範例
defmodule Example do
Module.defines?(__MODULE__, {:version, 0}, :def) #=> false
def version, do: 1
Module.defines?(__MODULE__, {:version, 0}, :def) #=> true
end
@spec defines_type?(module(), definition()) :: boolean()
檢查目前的模組是否定義指定的類型(私有、不透明或其他)。
此函數僅適用於正在編譯的模組。
@spec definitions_in(module()) :: [definition()]
傳回在 module
中定義的所有函式和巨集。
它傳回一個清單,其中包含所有已定義的函數和巨集(公開和私人),格式為 [{name, arity}, ...]
。
此函數只能用於尚未編譯的模組。使用 Module.__info__/1
回呼取得編譯模組中的公開函數和巨集。
範例
defmodule Example do
def version, do: 1
defmacrop test(arg), do: arg
Module.definitions_in(__MODULE__) #=> [{:version, 0}, {:test, 1}]
end
@spec definitions_in(module(), def_kind()) :: [definition()]
傳回在 module
中定義的所有函式,依據其種類。
此函數只能用於尚未編譯的模組。使用 Module.__info__/1
回呼取得編譯模組中的公開函數和巨集。
範例
defmodule Example do
def version, do: 1
Module.definitions_in(__MODULE__, :def) #=> [{:version, 0}]
Module.definitions_in(__MODULE__, :defp) #=> []
end
刪除指定模組屬性的項目(或項目)。
它傳回已刪除的屬性值。如果尚未設定屬性或未設定為累積,則傳回 nil
。
如果屬性設定為累積,則此函數總是傳回清單。刪除屬性會移除現有項目,但屬性仍會累積。
範例
defmodule MyModule do
Module.put_attribute(__MODULE__, :custom_threshold_for_lib, 10)
Module.delete_attribute(__MODULE__, :custom_threshold_for_lib)
end
@spec delete_definition(module(), definition()) :: boolean()
從模組中刪除定義。
如果定義存在且已移除,則傳回 true
;否則傳回 false
。
@spec eval_quoted( module() | Macro.Env.t(), Macro.t(), list(), keyword() | Macro.Env.t() ) :: term()
在指定的模組內容中評估引號內容。
環境選項清單也可以作為引數提供。請參閱 Code.eval_string/3
以取得更多資訊。
如果模組已編譯,則會引發錯誤。
範例
defmodule Foo do
contents =
quote do
def sum(a, b), do: a + b
end
Module.eval_quoted(__MODULE__, contents)
end
Foo.sum(1, 2)
#=> 3
為了方便,您可以傳遞任何 Macro.Env
結構,例如 __ENV__/0
,作為第一個引數或選項。模組和所有選項都將自動從環境中擷取
defmodule Foo do
contents =
quote do
def sum(a, b), do: a + b
end
Module.eval_quoted(__ENV__, contents)
end
Foo.sum(1, 2)
#=> 3
請注意,如果您傳遞 Macro.Env
結構作為第一個引數,同時也傳遞 opts
,它們將與 opts
合併,其中 opts
具有優先權。
從模組中取得指定的屬性。
如果屬性已使用 Module.register_attribute/3
標記為 accumulate
,則總是會傳回清單。如果屬性未標記為 accumulate
且未設定為任何值,則會傳回 nil
。
@
巨集編譯為呼叫此函式。例如,下列程式碼
@foo
擴充為類似於
Module.get_attribute(__MODULE__, :foo)
此函式只能用於尚未編譯的模組。使用 Module.__info__/1
回呼取得所有已儲存的屬性,或使用 Code.fetch_docs/1
擷取編譯模組中所有與文件相關的屬性。
範例
defmodule Foo do
Module.put_attribute(__MODULE__, :value, 1)
Module.get_attribute(__MODULE__, :value) #=> 1
Module.get_attribute(__MODULE__, :value, :default) #=> 1
Module.get_attribute(__MODULE__, :not_found, :default) #=> :default
Module.register_attribute(__MODULE__, :value, accumulate: true)
Module.put_attribute(__MODULE__, :value, 1)
Module.get_attribute(__MODULE__, :value) #=> [1]
end
@spec get_definition(module(), definition(), keyword()) :: {:v1, def_kind(), meta :: keyword(), [ {meta :: keyword(), arguments :: [Macro.t()], guards :: [Macro.t()], Macro.t()} ]} | nil
傳回指定名稱-元組對應的定義。
它傳回一個元組,其中包含 version
、kind
、定義 metadata
,以及一個清單,其中包含每個子句。每個子句都是一個四元素元組,其中包含 metadata、引數、防護和子句 AST。
這些子句會以 Elixir AST 的形式傳回,但子集已經展開並正規化。這對於分析程式碼很有用,但無法重新注入模組,因為它會失去一些原始的內容。由於這個 AST 表示法主要是內部的,因此它有版本,並且可能會隨時變更。因此,請小心使用這個 API。
選項
:skip_clauses
(自 v1.14.0 起) - 傳回[]
,而不是傳回子句。當只對取得種類和元資料有興趣時,這很有用
從模組中取得指定屬性的最後設定值。
如果屬性已標示為 accumulate
,並使用 Module.register_attribute/3
,則會傳回已設定的先前值。如果屬性沒有累積,則此呼叫與呼叫 Module.get_attribute/3
相同。
此函式只能用於尚未編譯的模組。使用 Module.__info__/1
回呼取得所有已儲存的屬性,或使用 Code.fetch_docs/1
擷取編譯模組中所有與文件相關的屬性。
範例
defmodule Foo do
Module.put_attribute(__MODULE__, :value, 1)
Module.get_last_attribute(__MODULE__, :value) #=> 1
Module.get_last_attribute(__MODULE__, :not_found, :default) #=> :default
Module.register_attribute(__MODULE__, :acc, accumulate: true)
Module.put_attribute(__MODULE__, :acc, 1)
Module.get_last_attribute(__MODULE__, :acc) #=> 1
Module.put_attribute(__MODULE__, :acc, 2)
Module.get_last_attribute(__MODULE__, :acc) #=> 2
end
檢查指定的屬性是否已定義。
如果屬性已使用 register_attribute/3
註冊或指定值,則會定義屬性。如果屬性已使用 delete_attribute/2
刪除,則不再視為已定義。
此函數只能用於尚未編譯的模組。
範例
defmodule MyModule do
@value 1
Module.register_attribute(__MODULE__, :other_value)
Module.put_attribute(__MODULE__, :another_value, 1)
Module.has_attribute?(__MODULE__, :value) #=> true
Module.has_attribute?(__MODULE__, :other_value) #=> true
Module.has_attribute?(__MODULE__, :another_value) #=> true
Module.has_attribute?(__MODULE__, :undefined) #=> false
Module.delete_attribute(__MODULE__, :value)
Module.has_attribute?(__MODULE__, :value) #=> false
end
@spec make_overridable(module(), [definition()]) :: :ok
@spec make_overridable(module(), module()) :: :ok
讓 module
中指定的函式可被覆寫。
可覆寫函式會延遲定義,讓開發人員可以自訂函式。請參閱 Kernel.defoverridable/1
以取得更多資訊和文件。
一旦函式或巨集標示為可覆寫,就不會再列在 definitions_in/1
下,或在傳給 defines?/2
時傳回 true,直到提供另一個實作為止。
檢查模組是否已開啟。
如果模組目前正在定義,且其屬性和函式可以修改,則模組為「開啟」狀態。
@spec overridable?(module(), definition()) :: boolean()
如果 module
中的 tuple
已在某個時間點標示為可覆寫,則傳回 true
。
請注意,即使定義已覆寫,overridable?/2
仍會傳回 true
。您可以使用 defines?/2
來查看定義是否存在或是否有待處理的定義。
傳回 module
中所有可覆寫的定義。
請注意,即使定義已被覆寫,定義仍會包含在內。你可以使用 defines?/2
來查看定義是否存在或是否正在處理中。
此函數只能用於尚未編譯的模組。
範例
defmodule Example do
def foo, do: 1
def bar, do: 2
defoverridable foo: 0, bar: 0
def foo, do: 3
[bar: 0, foo: 0] = Module.overridables_in(__MODULE__) |> Enum.sort()
end
在指定的 module
中放置一個具有 key
和 value
的模組屬性。
範例
defmodule MyModule do
Module.put_attribute(__MODULE__, :custom_threshold_for_lib, 10)
end
註冊一個屬性。
透過註冊屬性,開發人員可以自訂 Elixir 儲存和累積屬性值的方式。
選項
註冊屬性時,可以提供兩個選項
:accumulate
- 多次呼叫同一個屬性會累積,而不是覆寫前一個屬性。新的屬性總是會新增到累積清單的最上面。:persist
- 屬性會持續存在於 Erlang 抽象格式中。在與 Erlang 函式庫介接時很有用。
預設情況下,兩個選項都是 false
。一旦屬性設定為累積或持續存在,就不能再回復此行為。
範例
defmodule MyModule do
Module.register_attribute(__MODULE__, :custom_threshold_for_lib, accumulate: true)
@custom_threshold_for_lib 10
@custom_threshold_for_lib 20
@custom_threshold_for_lib #=> [20, 10]
end
@spec reserved_attributes() :: map()
傳回 Elixir 使用的模組屬性資訊。
請參閱模組文件中的「模組屬性」區段,以取得每個屬性的更多資訊。
範例
iex> map = Module.reserved_attributes()
iex> Map.has_key?(map, :moduledoc)
true
iex> Map.has_key?(map, :doc)
true
串接一個別名清單,並僅在別名已被參照時傳回新的別名。
如果別名尚未被參照,則會失敗並顯示 ArgumentError
。它會處理二進位資料和原子。
範例
iex> Module.safe_concat([List, Chars])
List.Chars
串接兩個別名,並僅在別名已被參照時傳回新的別名。
如果別名尚未被參照,則會失敗並顯示 ArgumentError
。它會處理二進位資料和原子。
範例
iex> Module.safe_concat(List, Chars)
List.Chars
@spec spec_to_callback(module(), definition()) :: boolean()
將指定的規範複製為一個回呼函式。
如果存在此規範且已複製為回呼,則傳回 true
。如果與規範相關聯的函式在呼叫此函式之前已定義文件,則也會複製文件。
將指定的模組名稱拆分為二進位元組。
module
必須是 Elixir 模組,因為 split/1
無法使用 Erlang 樣式的模組(例如,split(:lists)
會產生錯誤)。
split/1
也支援分割 Elixir 模組的字串表示(也就是呼叫 Atom.to_string/1
並使用模組名稱的結果)。
範例
iex> Module.split(Very.Long.Module.Name.And.Even.Longer)
["Very", "Long", "Module", "Name", "And", "Even", "Longer"]
iex> Module.split("Elixir.String.Chars")
["String", "Chars"]