查看源代码 模块 行为 (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.ac.cn/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

回调将按注册顺序运行。任何可覆盖的定义将在第一个回调运行之前变为具体。一个定义可以在另一个 before compile 回调中再次变为可覆盖,并且在所有回调运行之后,它将最后一次变为具体。

注意:回调函数/宏必须放置在单独的模块中(因为当回调被调用时,当前模块尚不存在)。

示例

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

The @ macro compiles to a call to this function. For example, the following code

@foo

Expands to something akin to

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 以及一个包含每个子句的列表。每个子句都是一个包含元数据、参数、守卫和子句 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()

从模块中获取给定属性的最后设置值。

如果属性使用 Module.register_attribute/3 标记为 accumulate,则将返回之前设置的值。如果属性不累积,则此调用与调用 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"]