查看源代码 模块 行为 (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.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
中放入具有 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
。
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
@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
以及一个包含每个子句的列表。每个子句都是一个包含元数据、参数、守卫和子句 AST 的四元组。
子句以 Elixir AST 返回,但它是已经扩展和规范化的一个子集。这使其适用于代码分析,但不能将其重新注入模块,因为它会丢失一些原始上下文。鉴于此 AST 表示主要用于内部,它是有版本控制的,并且可能随时更改。因此,**请谨慎使用此 API**。
选项
:skip_clauses
(自 v1.14.0 起) - 返回[]
而不是返回子句。当只想获取种类和元数据时,这很有用
从模块中获取给定属性的最后设置值。
如果属性使用 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
检查给定的属性是否已定义。
如果属性已使用 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"]