模块 | Module
模块
提供在编译期间处理模块的功能。
它允许开发人员动态添加、删除和注册属性,附加文档等等。
在编译一个模块之后,使用该模块中的许多函数将引发错误,因为检查运行时数据超出了它们的范围。大多数运行时数据可以通过__info__/1
函数附加到每个编译模块。
模块属性
每个模块可以使用一个或多个属性来修饰。Elixir目前定义了以下内容:
@after_compile
编译当前模块后立即调用的钩子。接受模块或{module, function_name}
请参阅下面的“编译回调”部分。
@before_compile
在编译模块之前调用的钩子。接受模块或{module, function_or_macro_name}
元组。请参阅下面的“编译回调”部分。
@behaviour (notice the British spelling)
行为可以被模块引用以确保它们实现由所定义的特定功能签名@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
为了帮助行为的正确实现,您可以选择声明@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
@impl true # will warn if neither Bar nor Baz specify a callback named bar/0
def bar(), do: :ok
@impl Baz # Will warn if Baz does not specify a callback named baz/0
def baz(), do: :ok
end
@compile
定义模块编译的选项。这用于配置Elixir和Erlang编译器,就像外部工具添加的任何其他编译传递一样。例如:
defmodule MyModule do
@compile {:inline, my_fun: 1}
def my_fun(arg) do
to_string(arg)
end
end
多种用途@compile
将累积,而不是覆盖以前的。请参阅下面的“编译选项”一节。
@doc
提供属性后面的函数或宏的文档。
接受一个字符串(通常是一个heredoc),或者false
在哪里@doc false
将使文档抽取工具(如ExDoc )对函数/宏不可见。例如:
defmodule MyModule do
@doc "Hello world"
def hello do
"world"
end
@doc """
Sums `a` to `b`.
"""
def sum(a, b) do
a + b
end
end
@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这样的工具可以使用这些信息来确保模块被重新编译,以防任何外部资源发生变化。
@file
更改属性后面的函数或宏的堆栈跟踪中使用的文件名,如:
defmodule MyModule do
@doc "Hello world"
@file "hello.ex"
def hello do
"world"
end
end
@moduledoc
提供当前模块的文档。
defmodule MyModule do
@moduledoc """
A very useful module.
"""
end
接受一个字符串(通常为定界符),或false
在那里@moduledoc false
将使得模块不可见的文件提取工具,如ExDoc。
@on_definition
在定义当前模块中的每个函数或宏时调用的钩子。在注释函数时很有用。
接受模块或{module, function_name}
元组。请参阅下面的“编译回调”部分。
@on_load
当加载模块时将被调用的钩子。
接受当前模块或{function_name, 0}
元组中函数的函数名称(作为原子),其中function_name
是当前模块中函数的名称。函数必须有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
Typspec属性
以下属性是类型筛选的一部分,也是由Elixir保留的:
@type
-定义要在@spec
@typep
-定义要在@spec
@opaque
-定义要在@spec
@spec
-为函数提供规范
@callback
-提供行为回调的规范
@macrocallback
-提供宏行为回调的规范
@optional_callbacks
-指定哪些行为回调和宏行为回调是可选的
@impl
-声明回调函数或宏的实现
自定义属性
除了上面概述的内置属性之外,还可以添加自定义属性。自定义属性是以@
然后是一个有效的Elixir值:
defmodule MyModule do
@custom_attr [some: "stuff"]
end
有关定义自定义属性时可用的更高级选项,请参阅register_attribute/3
。
编译回调
在定义函数时,以及在生成模块字节码之前和之后调用三个回调。
@after_compile
编译当前模块后立即调用的钩子。
接受模块或{module, function_name}
元组。该函数必须有两个参数:模块环境和字节码。当仅提供模块时,该功能被假定为__after_compile__/2
。
例
defmodule MyModule do
@after_compile __MODULE__
def __after_compile__(env, _bytecode) do
IO.inspect env
end
end
@before_compile
一个将在模块编译之前被调用的钩子。
接受模块或{module, function_or_macro_name}
元组。函数/宏必须使用一个参数:模块环境。如果它是一个宏,它的返回值将在编译开始之前在模块定义的末尾注入。
当只提供一个模块时,函数/宏被假定为__before_compile__/1
。
注意
:不同的是@after_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"
@on_definition
在定义当前模块中的每个函数或宏时调用的钩子。在注释函数时很有用。
接受模块或{module, function_name}
元组。该函数必须包含6个参数:
- 模块环境
- 那种功能/宏:
:def
,:defp
,:defmacro
,或者:defmacrop
- 函数/宏名称
- 引用的论点清单
- 引用的警卫名单
- 噪声函数体
注意hook接收引用的参数,并在函数存储在模块中之前调用它。所以Module.defines?/2
会返回false
每个函数的第一个子句。
如果定义的函数/宏具有多个子句,则将针对每个子句调用该钩子。
不像其他钩子,@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
编译选项
该@compile
属性接受Elixir和Erlang编译器使用的不同选项。下面列出了一些常见使用案例:
@compile :debug_info
-包括:debug_info
中的相应设置。Code.compiler_options/1
@compile {:debug_info, false}
-残疾:debug_info
中的相应设置。Code.compiler_options/1
@compile {:inline, some_fun: 2, other_fun: 3}
-插入给定的名称/特性对
@compile {:autoload, false}
-编译后禁用模块的自动加载。相反,模块将在被分派到
您可以在:compile
模块的文档中看到Erlang编译器使用的更多选项。
摘要
功能
__info__(kind)
提供有关模块定义的函数和宏的运行时信息,启用docstring提取等。
add_doc(module,line,kind,function_tuple,signature \ [],doc)
将文档附加到给定的函数或类型
concat(list)
连接别名列表并返回新别名。
concat(left, right)
连接两个别名并返回一个新别名。
create(module, quoted, opts)
创建一个具有给定名称并由给定的引号表达式定义的模块。
defines?(module, tuple)
检查模块是否定义了给定的函数或宏。
defines?(module, tuple, def_kind)
检查模块是否定义了给定的kind
definitions_in(module)
返回在中定义的所有函数 module
definitions_in(module, def_kind)
module
根据其种类返回所有定义的函数
delete_attribute(module, key)
删除与给定键匹配的模块属性。
eval_quoted(module_or_env, quoted, binding \ [], opts \ [])
计算给定模块上下文中引用的内容
get_attribute(module, key)
从模块中获取给定属性。
make_overridable(module, tuples)
使给定的函数在module
过脊
open?(module)
检查模块是否打开
overridable?(module, tuple)
如果tuple
in module
被标记为可覆盖,则返回true
put_attribute(module, key, value)
将模块属性放在key
和value
在给定的module
register_attribute(module, attribute, options)
注册属性
safe_concat(list)
只在别名已被引用时,才会生成别名列表,并返回新别名。
safe_concat(left, right)
Concatenates两个别名,并仅当别名已被引用时才返回新别名。
split(module)
将给定的模块名拆分为二进制部分。
功能
__info__(kind)
__info__(:attributes | :compile | :exports | :functions | :macros | :md5 | :module) ::
atom |
[{atom, any} | {atom, byte, integer}]
提供有关模块定义的函数和宏的运行时信息,启用docstring提取等。
每个模块在__info__/1
编译时都会得到一个函数。该函数采用以下原子之一:
:functions
-公共职能的关键词清单及其性质
:macros
-公共宏的关键字列表及其特性
- :module- 模块名称(Module == Module.__info__(:module))除上述内容外,您还可以传递给每个已编译模块所定义的__info__/1支持的原子。:erlang.module_info/0有关支持的属性和更多信息的列表,请参阅模块 - Erlang参考手册 .add_doc(module, line,kind,function_tuple,signature \ [],doc)add_doc(module,non_neg_integer,def_kind | type_kind,definition,list,String.t | boolean | nil):: ok | {:错误,:private_doc}文档上连接于给定功能或type.It期望的功能/类型属于,线(非负整数),种类(模块:def,:defmacro,:type,:opaque),元组{<function name>, <arity>},函数签名(签名应该省略的类型)和文档,它应该是二进制或布尔值。它返回:ok或{:error, :private_doc}。示例代码模块MyModule do Module.add_doc(__ MODULE__,__ENV __。line + 1,:def,{:version,0},[],“手动添加文档”)def version,do:1 endconcat(list)concat([binary | atom.coat([Foo,Bar]]):atomConcatenates一列别名并返回一个新的别名。例子> ,right)concat(binary | atom,binary | atom):: atomConcatenates两个别名并返回一个新的别名。例子> Module.concat(Foo,Bar)Foo.Bar iex> Module.concat(Foo,“Bar”)Foo .Barcreate(module,quoted,opts)create(module,Macro.t,Macro.Env.t | keyword):: {:module,module,binary,term}创建一个具有给定名称并由给定引用定义的模块表达式。模块定义的行和文件必须作为选项传递。示例内容=引用do def world,do:true end Module.create(Hello,contents,Macro.Env.location(__ ENV__))Hello.world#=> true与defmoduleModule.create/3作品中的差异类似,defmodule并返回相同的结果。虽然人们也可以defmodule动态地定义模块,但是当模块主体由带引号的表达式给出时,此函数是首选。另一个重要区别是,它Module.create/3允许您在定义模块时控制使用的环境变量,同时defmodule自动共享相同的环境。定义?(模块,元组)定义?(模块,定义):: booleanChecks如果模块定义了给定的函数或宏。请defines?/3使用为特定类型断言。此函数只能用于尚未编译的模块。使用Kernel.function_exported?/3检查已编译的模块.Examplesdefmodule示例是否执行Module.defines?__MODULE__,{:version,0}#=> false def version,do:1 Module.defines?__MODULE__,{:version,0}#=> true enddefines?(module,tuple,def_kind)定义了?(模块,定义,def_kind):: booleanChecks如果模块定义给定函数或宏kind。kind可以是任意的:def,:defp,:defmacro,或:defmacrop。这个功能只能在尚未编译的模块中使用。使用Kernel.function_exported?/3检查编译modules.Examplesdefmodule例做Module.defines?__MODULE__,{:version,0},:defp#=> false def version,do:1 Module.defines?__MODULE__,{:version,0},:defp#=> false enddefinitions_in(module)definitions_in(module):: [definition]返回所有在module.definitions_in(module,def_kind)定义_in(模块,def_kind):: [定义]返回所有定义在的函数module,根据以下定义:.definitions_in(module,def_kind)它的类。例子模块例子def def version,do:1 Module.definitions_in __MODULE__,:def#=> [{:version,0}] Module.definitions_in __MODULE__,::defp#=> [] enddelete_attribute(module,key)delete_attribute( module,atom):: term删除与给定key匹配的模块属性。它返回已删除的属性值(或nil如果没有设置).Examplesdefmodule MyModule do Module.put_attribute __MODULE__,:custom_threshold_for_lib,10 Module.delete_attribute __MODULE__,:custom_threshold_for_lib endeval_quoted(module_or_env,quoted,binding \ [],opts \ [])eval_quoted(module | Macro.Env.t ,Macro.t,list,keyword | Macro.Env.t):: termEvaluates给定模块上下文中的引用内容。环境选项列表也可以作为参数给出。请参阅Code.eval_string/3以获取更多信息。如果模块已经编译过,则产生错误。示例defmodule Foo do contents = quote do:(def sum(a,b),do:a + b)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)Module.eval_quoted __ENV__,contents end Foo.sum(1, 2)#=> 3请注意,如果您在传递Macro.Env结构时将它作为第一个参数传递opts,它们将与opts具有precedence.get_attribute(module,key)的get_attribute(module,atom):: termGets从模块中获取给定属性合并。属性被打上accumulate带Module.register_attribute/3,总是返回一个列表。nil如果属性没有被标记accumulate并且没有被设置为任何值,则返回@宏编译为对此函数的调用。例如,以下代码:@fooExpands类似于:Module.get_attribute(__ MODULE__,:foo)Examplesdefmodule Foo do Module.put_attribute __MODULE__,:value,1 Module.get_attribute __MODULE__,:value#=> 1 Module.register_attribute __MODULE__ ,:value,accumulate:true Module.put_attribute __MODULE__,:value,1 Module.get_attribute __MODULE__,:value#=> [1] endmake_overridable(module,tuples)make_overridable(module,module):::okmake_overridable(module,[definition ]):: ok确定module可重写的给定函数。可重写函数被懒惰地定义,允许开发人员对其进行自定义。看到Kernel.defoverridable/1获取更多信息和documentation.open?(module)open?(module):: booleanChecks如果一个模块是open.A模块是“open”,如果它当前正在被定义并且它的属性和功能可以被修改.overridable?(module ?,元组)可重写的(模块,定义):: booleanReturns true如果tuple在module被标记为overridable.put_attribute(模块,键,值)put_attribute(模块,原子,术语):::okPuts与模块属性key和value在给定的module.module MyModule doModule.put_attribute __MODULE__,:custom_threshold_for_lib,10 endregister_attribute(module,attribute,options)register_attribute(module,atom,[accumulate:boolean,persist:boolean]):::ok注册一个属性。通过注册属性,a开发人员可以定制Elixir如何存储和累积属性值。选项当注册一个属性时,可以给出两个选项:
:accumulate
-对同一属性的几次调用将累积起来,而不是覆盖前一个属性。新属性总是添加到累积列表的顶部。
:persist
-属性将以Erlang抽象格式持久化。在与Erlang库接口时很有用。
默认情况下,这两个选项都是false
。
实例
defmodule MyModule do
Module.register_attribute __MODULE__,
:custom_threshold_for_lib,
accumulate: true, persist: false
@custom_threshold_for_lib 10
@custom_threshold_for_lib 20
@custom_threshold_for_lib #=> [20, 10]
end
safe_concat(list)
safe_concat([binary | atom]) :: atom
只在别名已被引用时,才会生成别名列表,并返回新别名。
如果别名尚未被引用,则ArgumentError
。它处理charlists,二进制文件和原子。
实例
iex> Module.safe_concat([Module, Unknown])
** (ArgumentError) argument error
iex> Module.safe_concat([List, Chars])
List.Chars
safe_concat(left, right)
safe_concat(binary | atom, binary | atom) :: atom
Concatenates两个别名,并返回一个新的别名,只有当别名已经被引用。
如果还没有引用别名,则在ArgumentError
它处理字符列表、二进制文件和原子。
实例
iex> Module.safe_concat(Module, Unknown)
** (ArgumentError) argument error
iex> Module.safe_concat(List, Chars)
List.Chars
split(module)
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"]