Модуль
Предоставляет функции для работы с модулями во время компиляции.
Он позволяет разработчику динамически добавлять, удалять и регистрировать атрибуты, прикреплять документацию и так далее.
После компиляции модуля использование многих функций этого модуля вызовет ошибки, так как они не предназначены для проверки данных во время выполнения. Большинство данных во время выполнения можно проверить с помощью функции __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 будут накапливаться, а не перезаписывать предыдущие. См. раздел «Параметры компиляции» ниже.
@deprecated
Предоставляет причину устаревания функции. Например:
defmodule Keyword do
@deprecated "Use Kernel.length/1 instead"
def size(keyword) do
length(keyword)
end
end Компилятор mix автоматически ищет вызовы устаревших модулей и выдает предупреждения во время компиляции, вычисленные с помощью mix xref warnings.
Рекомендуется использовать эту функцию с осторожностью, особенно авторам библиотек. Устаревание кода всегда увеличивает нагрузку на пользователей библиотеки. Мы также рекомендуем поддерживать устаревшую функциональность в течение длительного периода времени даже после устаревания, что даст разработчикам достаточно времени для обновления (за исключением случаев, когда сохранение устаревшего API нежелательно, например, при наличии проблем с безопасностью).
@doc (и @since)
Предоставляет документацию для функции или макроса, следующего за атрибутом.
Принимает строку (часто heredoc) или false, где @doc false сделает функцию/макрос невидимой для инструментов извлечения документации, таких как ExDoc. Например:
defmodule MyModule do
@doc "Hello world"
@since "1.1.0"
def hello do
"world"
end
@doc """
Sums `a` to `b`.
"""
def sum(a, b) do
a + b
end
end @since — это необязательный атрибут, который указывает, в какой версии функция была представлена.
@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 Модули, скомпилированные с HiPE, не будут вызывать этот обработчик.
@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)
-
Предоставляет информацию о функциях и макросах, определённых модулем, и т.д.
- 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 | :functions | :macros | :md5 | :module | :deprecated ) :: any()
Предоставляет информацию о функциях и макросах, определённых модулем, и т.д.
Каждый модуль получает функцию __info__/1 при компиляции. Функция принимает один из следующих атомов:
-
:functions- список ключевых слов с общедоступными функциями и их арностями -
:macros- список ключевых слов с общедоступными макросами и их арностями -
:module- атом имени модуля -
:md5- MD5 модуля -
:compile- список с метаданными компилятора -
:attributes- список со всеми сохранёнными атрибутами
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()} Создаёт модуль с заданным именем и определённый заданными выражениями в виде котировок.
Строка, где определён модуль, и его файл должны быть переданы в качестве опций.
Возвращает кортеж вида {:module, module, binary, term} где module - имя модуля, binary - байткод модуля, и term - результат последнего выражения в quoted.
Аналогично Kernel.defmodule/2, бинарник будет записан на диск только как файл .beam если Module.create/3 вызывается в файле, который в данный момент компилируется.
Примеры
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 и возвращает те же результаты. Хотя можно также использовать defmodule для динамического определения модулей, эта функция предпочтительнее, когда тело модуля задаётся выражением в виде котировки.
Ещё одно важное различие заключается в том, что Module.create/3 позволяет управлять переменными окружения, используемыми при определении модуля, в то время как Kernel.defmodule/2 автоматически использует окружение, в котором она вызывается.
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(), module()) :: :ok
make_overridable(module(), [definition()]) :: :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. Обрабатывает списки символов, бинарные данные и атомы.
Примеры
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. Обрабатывает списки символов, бинарные данные и атомы.
Примеры
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.6.6/Module.html