Модуль
Предоставляет функции для работы с модулями во время компиляции.
Это позволяет разработчику динамически добавлять, удалять и регистрировать атрибуты, прикреплять документацию и так далее.
После компиляции модуля использование многих функций в этом модуле вызовет ошибки, так как они не предназначены для проверки данных во время выполнения. Большинство данных во время выполнения можно проверить с помощью функции __info__/1, прикрепленной к каждому скомпилированному модулю.
Атрибуты модуля
Каждый модуль может быть снабжён одним или несколькими атрибутами. Следующие определены в Elixir:
@after_compile
Обработчик, который вызывается сразу после компиляции текущего модуля. Принимает модуль или {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
Для помощи в корректной реализации поведений, вы можете опционально объявить @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
Принимает строку (часто heredoc) или 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
Атрибуты типов
Следующие атрибуты являются частью типов и также зарезервированы 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 - имя функции/макроса
- список цитированных аргументов
- список цитированных условий
- тело функции в цитатах
Обратите внимание, что обработчик получает цитированные аргументы, и он вызывается до того, как функция хранится в модуле. Таким образом, 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}- отключает автоматическую загрузку модулей после компиляции. Вместо этого модуль будет загружен после его отправки в
Вы можете увидеть несколько дополнительных параметров, используемых Erlang компилятором, в документации для :compile модуля.
Резюме
Функции
- __info__(kind)
-
Предоставляет сведения о функциях и макросах, определённых модулем во время выполнения, позволяет извлекать строку документации и т. д.
- 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)
-
Возвращает
true, еслиtupleвmoduleпомечен как переопределяемый. - put_attribute(module, key, value)
-
Записывает атрибут модуля со значениями
keyиvalueв заданномmodule. - register_attribute(module, attribute, options)
-
Регистрирует атрибут.
- safe_concat(list)
-
Конкатенирует список псевдонимов и возвращает новый псевдоним только в том случае, если псевдоним уже был упомянут.
- safe_concat(left, right)
-
Конкатенирует два псевдонима и возвращает новый псевдоним только в том случае, если псевдоним уже был упомянут.
- split(module)
-
Разбивает заданное имя модуля на бинарные части.
Функции
__info__(kind)
__info__(:attributes | :compile | :exports | :functions | :macros | :md5 | :module) ::
atom() |
[{atom(), any()} | {atom(), byte(), integer()}] Предоставляет информацию о функциях и макросах, определённых модулем во время выполнения, позволяет извлекать строку документации и т. д.
Каждый модуль получает функцию __info__/1 при компиляции. Функция принимает один из следующих атомов:
-
:functions- список ключевых слов общедоступных функций вместе с их арностью. -
:macros- список ключевых слов общедоступных макросов вместе с их арностью. -
:module- имя модуля (Module == Module.__info__(:module))
Помимо вышеперечисленного, вы также можете передать в __info__/1 любой атом, поддерживаемый :erlang.module_info/0, который также определяется для каждого скомпилированного модуля.
Список поддерживаемых атрибутов и дополнительная информация приведены в Modules – Erlang Reference Manual.
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 |
{:error, :private_doc} Прикрепляет документацию к заданной функции или типу.
Ожидается модуль, к которому принадлежит функция/тип, строка (неотрицательное целое число), тип (:def, :defmacro, :type, :opaque), кортеж {<function name>, <arity>}, сигнатура функции (сигнатура должна быть опущена для типов) и документация, которая должна быть либо двоичным значением, либо булевым.
Возвращает :ok или {:error, :private_doc}.
Примеры
defmodule MyModule do
Module.add_doc(__MODULE__, __ENV__.line + 1, :def, {:version, 0}, [], "Manually added docs")
def version, do: 1
end concat(list)
concat([binary() | atom()]) :: atom()
Конкатенирует список псевдонимов и возвращает новый псевдоним.
Примеры
iex> Module.concat([Foo, Bar]) Foo.Bar iex> Module.concat([Foo, "Bar"]) Foo.Bar
concat(left, right)
concat(binary() | atom(), binary() | atom()) :: atom()
Конкатенирует два псевдонима и возвращает новый псевдоним.
Примеры
iex> Module.concat(Foo, Bar) Foo.Bar iex> Module.concat(Foo, "Bar") Foo.Bar
create(module, quoted, opts)
create(module(), Macro.t(), Macro.Env.t() | keyword()) :: {:module, module(), binary(), term()} Создаёт модуль с заданным именем, определяемый заданными выражениями в кавычках.
Строка, в которой определён модуль, и его файл должны быть переданы в качестве параметров.
Примеры
contents =
quote do
def world, do: true
end
Module.create(Hello, contents, Macro.Env.location(__ENV__))
Hello.world #=> true Различия с defmodule
Module.create/3 работает аналогично defmodule и возвращает те же результаты. Хотя можно также использовать defmodule для динамического определения модулей, эта функция предпочтительнее, когда тело модуля задаётся выражением в кавычках.
Ещё одно важное отличие заключается в том, что Module.create/3 позволяет управлять переменными окружения, используемыми при определении модуля, в то время как defmodule автоматически использует те же переменные окружения.
defines?(module, tuple)
defines?(module(), definition()) :: boolean()
Проверяет, определяет ли модуль заданную функцию или макрос.
Используйте defines?/3 для проверки конкретного типа.
Эта функция может использоваться только для модулей, которые ещё не были скомпилированы. Используйте Kernel.function_exported?/3 для проверки скомпилированных модулей.
Примеры
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)
defines?(module(), definition(), def_kind()) :: boolean()
Проверяет, определяет ли модуль функцию или макрос заданного kind.
kind может быть любым из :def, :defp, :defmacro, или :defmacrop.
Эта функция может использоваться только для модулей, которые ещё не были скомпилированы. Используйте Kernel.function_exported?/3 для проверки скомпилированных модулей.
Примеры
defmodule Example do
Module.defines? __MODULE__, {:version, 0}, :defp #=> false
def version, do: 1
Module.defines? __MODULE__, {:version, 0}, :defp #=> false
end definitions_in(module)
definitions_in(module()) :: [definition()]
Возвращает все функции, определённые в module.
Примеры
defmodule Example do
def version, do: 1
Module.definitions_in __MODULE__ #=> [{:version, 0}]
end definitions_in(module, def_kind)
definitions_in(module(), def_kind()) :: [definition()]
Возвращает все функции, определённые в module, в соответствии с его видом.
Примеры
defmodule Example do
def version, do: 1
Module.definitions_in __MODULE__, :def #=> [{:version, 0}]
Module.definitions_in __MODULE__, :defp #=> []
end delete_attribute(module, key)
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
eval_quoted(module_or_env, quoted, binding \\ [], opts \\ [])
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) 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 имея приоритет.
get_attribute(module, key)
get_attribute(module(), atom()) :: term()
Получает заданный атрибут из модуля.
Если атрибут был помечен как accumulate с помощью Module.register_attribute/3, список всегда возвращается. nil возвращается, если атрибут не был помечен как accumulate и не был установлен в какое-либо значение.
Макрос @ компилируется в вызов этой функции. Например, следующий код:
@foo
Расширяется до чего-то подобного:
Module.get_attribute(__MODULE__, :foo)
Примеры
defmodule 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] end
make_overridable(module, tuples)
make_overridable(module(), [definition()]) :: :ok
make_overridable(module(), module()) :: :ok
Делает заданные функции в module переопределяемыми.
Переопределяемая функция определяется лениво, что позволяет разработчику настроить её. См. Kernel.defoverridable/1 для получения дополнительной информации и документации.
open?(module)
open?(module()) :: boolean()
Проверяет, открыт ли модуль.
Модуль считается «открытым», если он в настоящее время определяется и его атрибуты и функции могут быть изменены.
overridable?(module, tuple)
overridable?(module(), definition()) :: boolean()
Возвращает true, если tuple в module помечен как переопределяемый.
put_attribute(module, key, value)
put_attribute(module(), atom(), term()) :: :ok
Устанавливает атрибут модуля со значением key и value в заданном module.
Примеры
defmodule MyModule do Module.put_attribute __MODULE__, :custom_threshold_for_lib, 10 end
register_attribute(module, attribute, options)
register_attribute(module(), atom(), [accumulate: boolean(), persist: boolean()]) :: :ok
Регистрирует атрибут.
Регистрация атрибута позволяет разработчику настроить, как Elixir будет хранить и накапливать значения атрибутов.
Параметры
При регистрации атрибута можно указать два параметра:
-
:accumulate- несколько вызовов одного и того же атрибута будут накапливаться вместо переопределения предыдущего. Новые атрибуты всегда добавляются в начало накопленного списка. -
:persist- атрибут будет сохранен в формате Erlang Abstract Format. Полезно при взаимодействии с 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()
Конкатенирует два псевдонима и возвращает новый псевдоним только в том случае, если псевдоним уже был использован.
Если псевдоним ещё не был использован, происходит ошибка ArgumentError. Обрабатывает charlists, бинарные данные и атомы.
Примеры
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"]
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.5.3/Module.html