檢視原始碼 模組 行為 (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

你可以將 falsetrue 或特定行為傳遞給 @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 也接受關鍵字清單,作為提供有關實體的任意元資料的方法。 ExDocIEx 等工具可以使用此資訊顯示註解。常見的用例是 :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 中放置一個具有 keyvalue 的模組屬性。

傳回 Elixir 使用的模組屬性資訊。

串接一個別名清單,並僅在別名已被參照時傳回新的別名。

串接兩個別名,並僅在別名已被參照時傳回新的別名。

將指定的規範複製為一個回呼函式。

將指定的模組名稱拆分為二進位元組。

類型

@type def_kind() :: :def | :defp | :defmacro | :defmacrop
@type definition() :: {atom(), arity()}

回呼

@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 起) 如果模組定義結構,則依序為每個欄位

函數

連結到此函數

attributes_in(module)

檢視原始碼 (自 1.13.0 起)
@spec attributes_in(module()) :: [atom()]

傳回 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
@spec concat([binary() | atom()]) :: atom()

串接別名的清單並傳回新的別名。

它處理二進位和原子。

範例

iex> Module.concat([Foo, Bar])
Foo.Bar

iex> Module.concat([Foo, "Bar"])
Foo.Bar
@spec concat(binary() | atom(), binary() | atom()) :: atom()

串接兩個別名並傳回新的別名。

它處理二進位和原子。

範例

iex> Module.concat(Foo, Bar)
Foo.Bar

iex> Module.concat(Foo, "Bar")
Foo.Bar
連結到此函數

create(module, quoted, opts)

檢視原始碼
@spec create(module(), Macro.t(), Macro.Env.t() | keyword()) ::
  {:module, module(), binary(), term()}

使用指定的引號表示式建立一個模組,並指定名稱。

定義模組的行及其檔案必須作為選項傳遞。

它傳回形狀為 {:module, module, binary, term} 的元組,其中 module 是模組名稱,binary 是模組位元組碼,termquoted 中最後一個表達式的結果。

類似於 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?/3Kernel.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
連結到此函數

defines?(module, tuple, def_kind)

檢視原始碼
@spec defines?(module(), definition(), def_kind()) :: boolean()

檢查模組是否定義指定 kind 的函式或巨集。

kind 可以是 :def:defp:defmacro:defmacrop 中的任何一個。

此函數只能用於尚未編譯的模組。使用 Kernel.function_exported?/3Kernel.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
連結到此函數

defines_type?(module, definition)

檢視原始碼 (自 1.7.0 起)
@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
連結到此函數

definitions_in(module, kind)

檢視原始碼
@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
連結到此函數

delete_attribute(module, key)

檢視原始碼
@spec delete_attribute(module(), atom()) :: term()

刪除指定模組屬性的項目(或項目)。

它傳回已刪除的屬性值。如果尚未設定屬性或未設定為累積,則傳回 nil

如果屬性設定為累積,則此函數總是傳回清單。刪除屬性會移除現有項目,但屬性仍會累積。

範例

defmodule MyModule do
  Module.put_attribute(__MODULE__, :custom_threshold_for_lib, 10)
  Module.delete_attribute(__MODULE__, :custom_threshold_for_lib)
end
連結到此函數

delete_definition(module, arg)

檢視原始碼 (自 1.12.0 起)
@spec delete_definition(module(), definition()) :: boolean()

從模組中刪除定義。

如果定義存在且已移除,則傳回 true;否則傳回 false

連結到此函數

eval_quoted(module_or_env, quoted, binding \\ [], opts \\ [])

檢視原始碼
@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 具有優先權。

連結到此函數

get_attribute(module, key, default \\ nil)

檢視原始碼
@spec get_attribute(module(), atom(), term()) :: term()

從模組中取得指定的屬性。

如果屬性已使用 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
連結到此函數

get_definition(module, arg, options \\ [])

檢視原始碼 (自 1.12.0 起)
@spec get_definition(module(), definition(), keyword()) ::
  {:v1, def_kind(), meta :: keyword(),
   [
     {meta :: keyword(), arguments :: [Macro.t()], guards :: [Macro.t()],
      Macro.t()}
   ]}
  | nil

傳回指定名稱-元組對應的定義。

它傳回一個元組,其中包含 versionkind、定義 metadata,以及一個清單,其中包含每個子句。每個子句都是一個四元素元組,其中包含 metadata、引數、防護和子句 AST。

這些子句會以 Elixir AST 的形式傳回,但子集已經展開並正規化。這對於分析程式碼很有用,但無法重新注入模組,因為它會失去一些原始的內容。由於這個 AST 表示法主要是內部的,因此它有版本,並且可能會隨時變更。因此,請小心使用這個 API

選項

  • :skip_clauses (自 v1.14.0 起) - 傳回 [],而不是傳回子句。當只對取得種類和元資料有興趣時,這很有用
連結到此函數

get_last_attribute(module, key, default \\ nil)

檢視原始碼 (自 1.15.0 起)
@spec get_last_attribute(module(), atom(), term()) :: term()

從模組中取得指定屬性的最後設定值。

如果屬性已標示為 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
連結到此函數

has_attribute?(module, key)

檢視原始碼 (自 1.10.0 起)
@spec has_attribute?(module(), atom()) :: boolean()

檢查指定的屬性是否已定義。

如果屬性已使用 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
連結到此函數

make_overridable(module, tuples)

檢視原始碼
@spec make_overridable(module(), [definition()]) :: :ok
@spec make_overridable(module(), module()) :: :ok

module 中指定的函式可被覆寫。

可覆寫函式會延遲定義,讓開發人員可以自訂函式。請參閱 Kernel.defoverridable/1 以取得更多資訊和文件。

一旦函式或巨集標示為可覆寫,就不會再列在 definitions_in/1 下,或在傳給 defines?/2 時傳回 true,直到提供另一個實作為止。

@spec open?(module()) :: boolean()

檢查模組是否已開啟。

如果模組目前正在定義,且其屬性和函式可以修改,則模組為「開啟」狀態。

連結到此函數

overridable?(module, tuple)

檢視原始碼
@spec overridable?(module(), definition()) :: boolean()

如果 module 中的 tuple 已在某個時間點標示為可覆寫,則傳回 true

請注意,即使定義已覆寫,overridable?/2 仍會傳回 true。您可以使用 defines?/2 來查看定義是否存在或是否有待處理的定義。

連結到此函數

overridables_in(module)

檢視原始碼 (自 1.13.0 起)
@spec overridables_in(module()) :: [atom()]

傳回 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
連結到此函數

put_attribute(module, key, value)

檢視原始碼
@spec put_attribute(module(), atom(), term()) :: :ok

在指定的 module 中放置一個具有 keyvalue 的模組屬性。

範例

defmodule MyModule do
  Module.put_attribute(__MODULE__, :custom_threshold_for_lib, 10)
end
連結到此函數

register_attribute(module, attribute, options)

檢視原始碼
@spec register_attribute(module(), atom(), accumulate: boolean(), persist: boolean()) ::
  :ok

註冊一個屬性。

透過註冊屬性,開發人員可以自訂 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
連結到此函數

reserved_attributes()

檢視原始碼 (自 1.12.0 起)
@spec reserved_attributes() :: map()

傳回 Elixir 使用的模組屬性資訊。

請參閱模組文件中的「模組屬性」區段,以取得每個屬性的更多資訊。

範例

iex> map = Module.reserved_attributes()
iex> Map.has_key?(map, :moduledoc)
true
iex> Map.has_key?(map, :doc)
true
@spec safe_concat([binary() | atom()]) :: atom()

串接一個別名清單,並僅在別名已被參照時傳回新的別名。

如果別名尚未被參照,則會失敗並顯示 ArgumentError。它會處理二進位資料和原子。

範例

iex> Module.safe_concat([List, Chars])
List.Chars
@spec safe_concat(binary() | atom(), binary() | atom()) :: atom()

串接兩個別名,並僅在別名已被參照時傳回新的別名。

如果別名尚未被參照,則會失敗並顯示 ArgumentError。它會處理二進位資料和原子。

範例

iex> Module.safe_concat(List, Chars)
List.Chars
連結到此函數

spec_to_callback(module, definition)

檢視原始碼 (自 1.7.0 起)
@spec spec_to_callback(module(), definition()) :: boolean()

將指定的規範複製為一個回呼函式。

如果存在此規範且已複製為回呼,則傳回 true。如果與規範相關聯的函式在呼叫此函式之前已定義文件,則也會複製文件。

@spec split(module() | String.t()) :: [String.t(), ...]

將指定的模組名稱拆分為二進位元組。

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"]