Модуль
Предоставляет функции для работы с модулями во время компиляции.
Позволяет разработчику динамически добавлять, удалять и регистрировать атрибуты, прикреплять документацию и так далее.
После компиляции модуля использование многих функций в этом модуле вызовет ошибки, так как они не предназначены для проверки данных во время выполнения. Большинство данных во время выполнения можно проверить с помощью функции __info__(attr), прикрепленной к каждому скомпилированному модулю.
Атрибуты модуля
Каждый модуль может быть снабжён одним или несколькими атрибутами. Следующие атрибуты в настоящее время определены в Elixir:
-
@after_compileОбработчик, который вызывается сразу после компиляции текущего модуля. Принимает модуль или кортеж
{<module>, <function atom>}. См. раздел «Обработчики компиляции» ниже. -
@before_compileОбработчик, который вызывается перед компиляцией модуля. Принимает модуль или кортеж
{<module>, <function/macro atom>}. См. раздел «Обработчики компиляции» ниже. -
@behaviour(обратите внимание на британское написание)Модули могут ссылаться на поведения, чтобы гарантировать, что они реализуют необходимые специфические сигнатуры функций, определённые
@callback.Например, можно указать поведение URI.Parser следующим образом:
defmodule URI.Parser do @doc "Parses the given URL" @callback parse(uri_info :: URI.t) :: URI.t @doc "Defines a default port" @callback default_port() :: integer end And then a module may use it as: defmodule URI.HTTP do @behaviour URI.Parser def default_port(), do: 80 def parse(info), do: info end
Если поведение изменится или URI.HTTP не реализует один из обращений, будет выведено предупреждение.
Указывает OTP или пользовательское поведение.
Пример
defmodule M do @behaviour :gen_event # ... end
-
@callback,@macrocallback, и@optional_callbacksЭти атрибуты используются для определения поведения (как показано в документации для
@behaviourвыше).@callbackопределяет обращение функции,@macrocallbackопределяет обращение макроса, а@optional_callbacksуказывает, какие обращения и макрообращения являются необязательными. -
@compileОпределяет параметры для компиляции модуля. Используется для настройки как Elixir, так и Erlang компиляторов, а также любых других этапов компиляции, добавленных сторонними инструментами.
Несколько применений
@compileбудут накапливаться вместо перезаписи предыдущих. См. раздел «Параметры компиляции» ниже.Пример
defmodule M do @compile {:inline, myfun: 1} def myfun(arg) do to_string(arg) end end -
@docПредоставляет документацию для функции или макроса, который следует за атрибутом.
Принимает строку (часто heredoc) или
false, где@doc falseсделает функцию/макрос невидимой для инструментов извлечения документации, таких как ExDoc.Можно вызывать более одного раза.
Пример
defmodule M 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, поддерживающей атрибуты модулей.Принимает атом, кортеж или список атомов и кортежей.
Список поддерживаемых предупреждений см. в
:dialyzerмодуле.Несколько применений
@dialyzerбудут накапливаться вместо перезаписи предыдущих.Пример
defmodule M do @dialyzer {:nowarn_function, myfun: 1} def myfun(arg) do M.not_a_function(arg) end end -
@external_resourceУказывает внешний ресурс для текущего модуля.
Часто модуль встраивает информацию из внешнего файла. Этот атрибут позволяет модулю аннотировать используемые внешние ресурсы.
Инструменты, такие как Mix, могут использовать эту информацию, чтобы гарантировать, что модуль будет перекомпилирован в случае изменения каких-либо внешних ресурсов.
-
@fileИзменяет имя файла, используемое в трассировках стека для функции или макроса, который следует за атрибутом.
Принимает строку. Можно использовать более одного раза.
Пример
defmodule M do @doc "Hello world" @file "hello.ex" def hello do "world" end end -
@moduledocПредоставляет документацию для текущего модуля.
Принимает строку (часто heredoc) или
false, где@moduledoc falseсделает модуль невидимым для инструментов извлечения документации, таких как ExDoc.Пример
defmodule M do @moduledoc """ A very useful module """ end
-
@on_definitionОбработчик, который будет вызван при определении каждой функции или макроса в текущем модуле. Полезно при аннотировании функций.
Принимает модуль или кортеж
{<module>, <function atom>}. См. раздел «Обработчики компиляции» ниже. -
@on_loadОбработчик, который будет вызван при загрузке модуля.
Принимает атом функции из текущего модуля. Функция должна иметь арность 0 (без аргументов) и должна возвращать
:ok, в противном случае загрузка модуля будет прервана.Пример
defmodule M do @on_load :load_check def load_check do if some_condition() do :ok else nil end end def some_condition do false end end -
@vsnУказывает версию модуля. Принимает любое допустимое значение Elixir.
Пример
defmodule M do @vsn "1.0" end
Следующие атрибуты являются частью типов и также зарезервированы в Elixir:
-
@type- определяет тип, используемый в@spec -
@typep- определяет закрытый тип, используемый в@spec -
@opaque- определяет непрозрачный тип, используемый в@spec -
@spec- предоставляет спецификацию для функции -
@callback- предоставляет спецификацию для обращения поведения -
@macrocallback- предоставляет спецификацию для обращения макроса поведения -
@optional_callbacks- указывает, какие обращения поведения и макрообращения поведения являются необязательными
Помимо встроенных атрибутов, перечисленных выше, можно также добавлять пользовательские атрибуты. Пользовательский атрибут — это любой допустимый идентификатор, начинающийся с @ и сопровождаемый допустимым значением Elixir:
defmodule M do @custom_attr [some: "stuff"] end
Дополнительные параметры, доступные при определении пользовательских атрибутов, см. register_attribute/3.
Обработчики компиляции
Существует три обработчика, которые вызываются при определении функций, а также перед и сразу после генерации байткода модуля.
@after_compile
Обработчик, который будет вызван сразу после компиляции текущего модуля.
Принимает модуль или кортеж {<module>, <function atom>}. Функция должна принимать два аргумента: окружение модуля и его байт код. Если предоставлен только модуль, функция предполагается __after_compile__/2.
Пример
defmodule M do
@after_compile __MODULE__
def __after_compile__(env, _bytecode) do
IO.inspect env
end
end @before_compile
Обработчик, который будет вызван перед компиляцией модуля.
Принимает модуль или кортеж {<module>, <function/macro atom>}. Функция/макрос должны принять один аргумент: окружение модуля. Если это макрос, возвращаемое значение будет вставлено в конце определения модуля перед началом компиляции.
Если предоставлен только модуль, функция/макрос предполагается __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 @on_definition
Обработчик, который будет вызван при определении каждой функции или макроса в текущем модуле. Полезно при аннотировании функций.
Принимает модуль или кортеж {<module>, <function atom>}. Функция должна принять 6 аргументов:
- окружение модуля
- тип:
:def,:defp,:defmacro, или:defmacrop - имя функции/макроса
- список цитируемых аргументов
- список цитируемых условий
- цитируемое тело функции
Обратите внимание, что обработчик получает цитируемые аргументы, и он вызывается до того, как функция сохранится в модуле. Таким образом, Module.defines?/2 вернёт false для первого условия каждой функции.
Если функция/макрос, которые определяются, имеют несколько условий, обработчик будет вызван для каждого условия.
В отличие от других обработчиков, @on_definition будет вызывать только функции и никогда не макросы. Это потому, что обработчик вызывается внутри контекста функции (и вложенные определения функций не разрешены в Elixir).
Если предоставлен только модуль, функция предполагается __on_definition__/6.
Пример
defmodule H 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 M do
@on_definition {H, :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 -
@compile {:debug_info, false}- отключает:debug_infoнезависимо от настроек вCode.compiler_options -
@compile {:inline, some_fun: 2, other_fun: 3}- встраивает указанные пары имя/арность -
@compile {:autoload, false}- отключает автоматическую загрузку модулей после компиляции. Вместо этого модуль будет загружен после его отправки в
Дополнительные параметры, используемые Erlang компилятором, можно посмотреть в документации для :compile модуля.
Резюме
Функции
- __info__(kind)
-
Предоставляет информацию о функциях и макросах, определенных модулем во время выполнения, позволяет извлечь строку документации и т. д.
- add_doc(module, line, kind, tuple, signature \\ [], doc)
-
Присоединяет документацию к заданной функции или типу.
- concat(list)
-
Конкатенирует список псевдонимов и возвращает новый псевдоним.
- concat(left, right)
-
Конкатенирует два псевдонима и возвращает новый псевдоним.
- create(module, quoted, opts)
-
Создает модуль с заданным именем и определенным заданными выражениями в кавычках.
- defines?(module, tuple)
-
Проверяет, определяет ли модуль заданную функцию или макрос.
- defines?(module, tuple, kind)
-
Проверяет, определяет ли модуль функцию или макрос заданного
kind. - definitions_in(module)
-
Возвращает все функции, определенные в
module. - definitions_in(module, kind)
-
Возвращает все функции, определенные в
module, в соответствии с его типом. - delete_attribute(module, key)
-
Удаляет все атрибуты, которые соответствуют заданному ключу.
- eval_quoted(module, 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)
-
Добавляет атрибут Erlang в заданный модуль с заданным ключом и значением.
- register_attribute(module, new, opts)
-
Регистрирует атрибут. Регистрация атрибута позволяет разработчику настроить то, как Elixir будет хранить и накапливать значения атрибутов.
- safe_concat(list)
-
Конкатенирует список псевдонимов и возвращает новый псевдоним только в том случае, если псевдоним уже упоминался.
- safe_concat(left, right)
-
Конкатенирует два псевдонима и возвращает новый псевдоним только в том случае, если псевдоним уже упоминался.
- split(module)
-
Разбивает заданное имя модуля на бинарные части.
Функции
__info__(kind)
__info__(:attributes | :compile | :exports | :functions | :macros | :md5 | :module | :native_addresses) ::
atom |
[{atom, any} | {atom, byte, integer}] Предоставляет информацию о функциях и макросах, определенных модулем во время выполнения, позволяет извлечь строку документации и т. д.
Каждый модуль получает функцию __info__/1 при компиляции. Функция принимает одно из следующих атомов:
-
:functions- список ключевых слов общедоступных функций вместе с их арностью. -
:macros- список ключевых слов общедоступных макросов вместе с их арностью. -
:module- имя модуля (Module == Module.__info__(:module)).
В дополнение к вышеперечисленному, вы также можете передать в __info__/1 любой атом, поддерживаемый :erlang.module_info/0, который также определен для каждого скомпилированного модуля.
Список поддерживаемых атрибутов и дополнительная информация см. в Модули – Справочник по Erlang.
add_doc(module, line, kind, tuple, signature \\ [], doc)
Присоединяет документацию к заданной функции или типу.
Ожидается модуль, к которому принадлежит функция/тип, строка (целое неотрицательное число), тип (def или defmacro), кортеж, представляющий функцию и ее арность, подпись функции (подпись должна быть опущена для типов) и документация, которая должна быть либо двоичным значением, либо булевым.
Примеры
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)
Создает модуль с заданным именем и определенным заданными выражениями в кавычках.
Строка, где определяется модуль, и его файл должны быть переданы в качестве опций.
Примеры
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?/3, чтобы проверить конкретный тип.
Примеры
defmodule Example do
Module.defines? __MODULE__, {:version, 0} #=> false
def version, do: 1
Module.defines? __MODULE__, {:version, 0} #=> true
end defines?(module, tuple, kind)
Проверяет, определяет ли модуль функцию или макрос заданного kind.
kind может быть любым из :def, :defp, :defmacro или :defmacrop.
Примеры
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)
Возвращает все функции, определенные в module.
Примеры
defmodule Example do
def version, do: 1
Module.definitions_in __MODULE__ #=> [{:version, 0}]
end definitions_in(module, kind)
Возвращает все функции, определенные в 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(atom, atom) :: :ok
Удаляет все атрибуты, которые соответствуют заданному ключу.
Примеры
defmodule MyModule do Module.put_attribute __MODULE__, :custom_threshold_for_lib, 10 Module.delete_attribute __MODULE__, :custom_threshold_for_lib end
eval_quoted(module, quoted, binding \\ [], opts \\ [])
Вычисляет содержимое в кавычках в контексте заданного модуля.
Список опций среды также может быть передан в качестве аргумента. См. 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
Для удобства вы можете передать __ENV__ в качестве аргумента, и все опции будут автоматически извлечены из среды:
defmodule Foo do contents = quote do: (def sum(a, b), do: a + b) Module.eval_quoted __MODULE__, contents, [], __ENV__ end Foo.sum(1, 2) #=> 3
get_attribute(module, key)
get_attribute(atom, 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)
Делает заданные функции в module переопределяемыми.
Переопределяемая функция определяется лениво, что позволяет разработчику настроить ее. Дополнительную информацию и документацию см. в Kernel.defoverridable/1.
open?(module)
Проверяет, открыт ли модуль, т.е. он в настоящее время определяется, и его атрибуты и функции могут быть изменены.
overridable?(module, tuple)
Возвращает true если tuple в module помечен как переопределяемый.
put_attribute(module, key, value)
Добавляет атрибут Erlang в указанный модуль с заданным ключом и значением.
Семантика добавления атрибута зависит от того, был ли он зарегистрирован или нет с помощью register_attribute/3.
Примеры
defmodule MyModule do Module.put_attribute __MODULE__, :custom_threshold_for_lib, 10 end
register_attribute(module, new, opts)
Регистрирует атрибут. Регистрация атрибута позволяет разработчику настроить, как 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 | no_return
Конкатенирует список псевдонимов и возвращает новый псевдоним только в том случае, если псевдоним уже был использован.
Если псевдоним ещё не был использован, возникает ошибка 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 | no_return
Конкатенирует два псевдонима и возвращает новый псевдоним только в том случае, если псевдоним уже был использован.
Если псевдоним ещё не был использован, возникает ошибка ArgumentError. Обрабатывает списки символов, бинарные данные и атомы.
Примеры
iex> Module.safe_concat(Module, Unknown) ** (ArgumentError) argument error iex> Module.safe_concat(List, Chars) List.Chars
split(module)
Разделяет имя модуля на бинарные части.
Примеры
iex> Module.split Very.Long.Module.Name.And.Even.Longer ["Very", "Long", "Module", "Name", "And", "Even", "Longer"]
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.3.4/Module.html