Код
Утилиты для управления компиляцией кода, оценкой кода и загрузкой кода.
Этот модуль дополняет модуль Erlang :code, добавляя поведение, специфичное для Elixir. Почти все функции в этом модуле имеют глобальные побочные эффекты на поведение Elixir.
Работа с файлами
Этот модуль содержит три функции для компиляции и оценки файлов. Вот краткое описание их и их поведения:
-
require_file/2— компилирует файл и отслеживает его имя. Он не компилирует файл повторно, если он уже был запрошен. -
compile_file/2— компилирует файл без отслеживания его имени. Компилирует файл несколько раз при каждом вызове. -
eval_file/2— оценивает содержимое файла без отслеживания его имени. Возвращает результат последнего выражения в файле, а не модули, определенные в нём.
Вкратце, первая функция должна использоваться, когда вы хотите отслеживать файлы, обрабатываемые системой, чтобы избежать повторной компиляции одного и того же файла. Это обычно используется в скриптах.
compile_file/2 необходимо использовать, когда вас интересуют модули, определенные в файле, без отслеживания. eval_file/2 следует использовать, когда вас интересует результат оценки файла, а не модули, которые он определяет.
Краткое описание
Функции
- append_path(path)
Добавляет путь в конец списка путей кода Erlang VM.
- available_compiler_options()
Возвращает список доступных опций компилятора.
- compile_file(file, relative_to \\ nil)
Компилирует указанный файл.
- compile_quoted(quoted, file \\ "nofile")
Компилирует выражение в виде quoted.
- compile_string(string, file \\ "nofile")
Компилирует заданную строку.
- compiler_options()
Получает опции компиляции из сервера кода.
- compiler_options(opts)
Устанавливает опции компиляции.
- delete_path(path)
Удаляет путь из списка путей кода Erlang VM. Это список каталогов, которые Erlang VM использует для поиска кода модулей.
- ensure_compiled(module)
Обеспечивает, что указанный модуль скомпилирован и загружен.
- ensure_compiled?(module)
Обеспечивает, что указанный модуль скомпилирован и загружен.
- ensure_loaded(module)
Обеспечивает, что указанный модуль загружен.
- ensure_loaded?(module)
Обеспечивает, что указанный модуль загружен.
- eval_file(file, relative_to \\ nil)
Вычисляет указанный файл.
- eval_quoted(quoted, binding \\ [], opts \\ [])
Вычисляет содержимое в виде quoted.
- eval_string(string, binding \\ [], opts \\ [])
Вычисляет содержимое, заданное
string.- fetch_docs(module_or_path)
Возвращает документацию для заданного модуля или пути к
.beamфайлу.- format_file!(file, opts \\ [])
Форматирует файл.
- format_string!(string, opts \\ [])
Форматирует данный код
string.- get_docs(module, kind) устаревший
Устаревшая функция для получения старого формата документации.
- prepend_path(path)
Добавляет путь в начало списка путей кода Erlang VM.
- purge_compiler_modules()
Очищает модули компилятора.
- require_file(file, relative_to \\ nil)
Загружает указанный
file.- required_files()
Список всех загруженных файлов.
- string_to_quoted(string, opts \\ [])
Преобразует данную строку в её форму quoted.
- string_to_quoted!(string, opts \\ [])
Преобразует данную строку в её форму quoted.
- unrequire_files(files)
Удаляет файлы из списка загруженных файлов.
Функции
append_path(path)
Спецификации
append_path(Path.t()) :: true | {:error, :bad_directory} Добавляет путь в конец списка путей кода Erlang VM.
Это список директорий, используемых Erlang VM для поиска кода модулей.
Путь расширяется с помощью Path.expand/1 перед добавлением. Если такого пути не существует, возвращается ошибка.
Примеры
Code.append_path(".")
#=> true
Code.append_path("/does_not_exist")
#=> {:error, :bad_directory} available_compiler_options()
Спецификации
available_compiler_options() :: [atom()]
Возвращает список доступных опций компилятора.
См. compiler_options/1 для получения дополнительной информации.
Примеры
Code.available_compiler_options() #=> [:docs, :debug_info, ...]
compile_file(file, relative_to \\ nil)
Спецификации
compile_file(binary(), nil | binary()) :: [{module(), binary()}] Компилирует указанный файл.
Принимает relative_to в качестве аргумента, чтобы указать местоположение файла.
Возвращает список кортежей, где первый элемент — имя модуля, а второй — его байткод (как двоичный). В отличие от require_file/2, он не отслеживает имя файла скомпилированного файла.
Если вам нужно получить результат вычисления файла, а не модулей, определённых в нём, см. eval_file/2.
Для одновременной компиляции нескольких файлов см. Kernel.ParallelCompiler.compile/2.
compile_quoted(quoted, file \\ "nofile")
Спецификации
compile_quoted(Macro.t(), binary()) :: [{module(), binary()}] Компилирует процитированное выражение.
Возвращает список кортежей, где первый элемент — имя модуля, а второй — его байткод (как двоичный). В качестве второго аргумента может быть передан file, который будет использован для отчётности о предупреждениях и ошибках.
compile_string(string, file \\ "nofile")
Спецификации
compile_string(List.Chars.t(), binary()) :: [{module(), binary()}] Компилирует указанную строку.
Возвращает список кортежей, где первый элемент — имя модуля, а второй — его байткод (как двоичный). В качестве второго аргумента может быть передан file, который будет использован для отчётности о предупреждениях и ошибках.
Предупреждение: string может быть любым кодом Elixir, и код может быть выполнен с теми же правами, что и Erlang VM: это означает, что такой код может скомпрометировать машину (например, выполняя системные команды). Не используйте compile_string/2 с недоверенным вводом (таким как строки, полученные из сети).
compiler_options()
Спецификации
compiler_options() :: %{optional(atom()) => boolean()} Получает опции компиляции из сервера кода.
См. compiler_options/1 для получения дополнительной информации.
Примеры
Code.compiler_options()
#=> %{debug_info: true, docs: true, ...} compiler_options(opts)
Спецификации
compiler_options(Enumerable.t()) :: %{optional(atom()) => boolean()} Устанавливает опции компиляции.
Эти опции являются глобальными, поскольку они хранятся сервером кода Elixir.
Доступные опции:
-
:docs- когдаtrue, сохранять документацию в скомпилированном модуле. По умолчаниюtrue. -
:debug_info- когдаtrue, сохранять отладочную информацию в скомпилированном модуле. Это позволяет разработчику восстановить исходный код. По умолчаниюfalse. -
:ignore_module_conflict- когдаtrue, перезаписывать уже определённые модули без выдачи ошибок. По умолчаниюfalse. -
:relative_paths- когдаtrue, использовать относительные пути в процитированных узлах, предупреждениях и ошибках, сгенерированных компилятором. Обратите внимание, что отключение этой опции не повлияет на предупреждения и ошибки во время выполнения. По умолчаниюtrue. -
:warnings_as_errors- приводит к ошибке компиляции при возникновении предупреждений. По умолчаниюfalse.
Возвращает новую карту опций компилятора.
Примеры
Code.compiler_options(debug_info: true)
#=> %{debug_info: true, docs: true,
#=> warnings_as_errors: false, ignore_module_conflict: false} delete_path(path)
Спецификации
delete_path(Path.t()) :: boolean()
Удаляет путь из списка путей кода Erlang VM. Это список директорий, используемых Erlang VM для поиска кода модулей.
Путь расширяется с помощью Path.expand/1 перед удалением. Если путь не существует, эта функция возвращает false.
Примеры
Code.prepend_path(".")
Code.delete_path(".")
#=> true
Code.delete_path("/does_not_exist")
#=> false ensure_compiled(module)
Спецификации
ensure_compiled(module()) ::
{:module, module()}
| {:error, :embedded | :badfile | :nofile | :on_load_failure} Обеспечивает компиляцию и загрузку данного модуля.
Если модуль уже загружен, он работает как no-op. Если модуль ещё не загружен, проверяется, нужно ли его скомпилировать, а затем пытается загрузить его.
Если модуль успешно загружен, возвращается {:module, module}. Если нет, возвращается {:error, reason} с причиной ошибки.
Если проверяемый модуль находится в текущей блокировке компилятора, эта функция возвращает {:error, :nofile}.
См. ensure_loaded/1 для получения дополнительной информации о загрузке модулей и о том, когда использовать ensure_loaded/1 или ensure_compiled/1.
ensure_compiled?(module)
Спецификации
ensure_compiled?(module()) :: boolean()
Обеспечивает компиляцию и загрузку данного модуля.
Аналогично ensure_compiled/1, но возвращает true если модуль уже загружен или был успешно загружен и скомпилирован. В противном случае возвращает false.
ensure_loaded(module)
Спецификации
ensure_loaded(module()) ::
{:module, module()}
| {:error, :embedded | :badfile | :nofile | :on_load_failure} Обеспечивает загрузку данного модуля.
Если модуль уже загружен, это работает как no-op. Если модуль ещё не загружен, он пытается загрузить его.
Если модуль успешно загружен, возвращается {:module, module}. Если нет, возвращается {:error, reason} с причиной ошибки.
Загрузка кода в Erlang VM
Erlang имеет два режима загрузки кода: интерактивный и встроенный.
По умолчанию Erlang VM работает в интерактивном режиме, где модули загружаются по мере необходимости. Во встроенном режиме происходит обратное, так как все модули должны быть загружены заранее или явно.
Поэтому эта функция используется для проверки, загружен ли модуль перед его использованием, и позволяет отреагировать соответствующим образом. Например, модуль URI использует эту функцию для проверки, существует ли определённый парсер для заданной схемы URI.
ensure_compiled/1
Elixir также содержит функцию ensure_compiled/1, которая является надмножеством ensure_loaded/1.
Поскольку компиляция в Elixir происходит параллельно, в некоторых ситуациях может потребоваться использовать модуль, который ещё не был скомпилирован, поэтому его даже нельзя загрузить.
При вызове ensure_compiled/1 останавливает компиляцию вызывающей функции, пока модуль, переданный ensure_compiled/1, не станет доступен или все файлы для текущего проекта не будут скомпилированы. Если компиляция завершается, а модуль недоступен, возвращается кортеж ошибки.
ensure_compiled/1 не относится к зависимостям, так как зависимости должны быть скомпилированы заранее.
В большинстве случаев ensure_loaded/1 достаточно. ensure_compiled/1 необходимо использовать в редких случаях, обычно связанных с макросами, которым требуется вызвать модуль для получения информации о обратных вызовах.
Примеры
iex> Code.ensure_loaded(Atom)
{:module, Atom}
iex> Code.ensure_loaded(DoesNotExist)
{:error, :nofile} ensure_loaded?(module)
Спецификации
ensure_loaded?(module()) :: boolean()
Обеспечивает загрузку данного модуля.
Аналогично ensure_loaded/1, но возвращает true если модуль уже загружен или был успешно загружен. В противном случае возвращает false.
Примеры
iex> Code.ensure_loaded?(Atom) true
eval_file(file, relative_to \\ nil)
Спецификации
eval_file(binary(), nil | binary()) :: {term(), binding :: list()} Вычисляет указанный файл.
Принимает relative_to в качестве аргумента, чтобы указать местоположение файла.
В то время как require_file/2 и compile_file/2 возвращают загруженные модули и их байткод, eval_file/2 просто вычисляет содержимое файла и возвращает результат вычисления и его связи (точно такой же возвращаемый результат, как eval_string/3).
eval_quoted(quoted, binding \\ [], opts \\ [])
Спецификации
eval_quoted(Macro.t(), list(), Macro.Env.t() | keyword()) ::
{term(), binding :: list()} Вычисляет содержимое, заключенное в кавычки.
Предупреждение: Вызов этой функции внутри макроса считается плохой практикой, так как она будет пытаться оценить значения во время выполнения во время компиляции. Аргументы макроса обычно преобразуются путем исключения кавычек в возвращаемые выражения (а не путем вычисления).
См. eval_string/3 для описания связываний и параметров.
Примеры
iex> contents = quote(do: var!(a) + var!(b))
iex> Code.eval_quoted(contents, [a: 1, b: 2], file: __ENV__.file, line: __ENV__.line)
{3, [a: 1, b: 2]} Для удобства вы можете передать __ENV__/0 в качестве аргумента opts и все параметры будут автоматически извлечены из текущей среды:
iex> contents = quote(do: var!(a) + var!(b))
iex> Code.eval_quoted(contents, [a: 1, b: 2], __ENV__)
{3, [a: 1, b: 2]} eval_string(string, binding \\ [], opts \\ [])
Спецификации
eval_string(List.Chars.t(), list(), Macro.Env.t() | keyword()) ::
{term(), binding :: list()} Вычисляет содержимое, заданное string.
Аргумент binding — это список ключевых слов переменных связей. Аргумент opts — это список ключевых слов параметров среды.
Предупреждение: string может быть любым кодом Elixir и будет выполняться с теми же привилегиями, что и Erlang VM: это означает, что такой код может скомпрометировать машину (например, выполняя системные команды). Не используйте eval_string/3 с ненадежными данными (например, строками, поступающими из сети).
Параметры
Параметры могут быть:
-
:file— файл, который необходимо учитывать при оценке -
:line— строка, на которой начинается сценарий
Кроме того, можно настроить следующие значения области видимости:
-
:aliases— список кортежей с псевдонимом и его целевым объектом -
:requires— список необходимых модулей -
:functions— список кортежей, где первый элемент — модуль, а второй — список импортированных имен функций и арности; список имен функций и арности должен быть отсортирован -
:macros— список кортежей, где первый элемент — модуль, а второй — список импортированных имен макросов и арности; список имен функций и арности должен быть отсортирован
Обратите внимание, что установка любого из вышеперечисленных значений переопределяет значения по умолчанию Elixir. Например, установка :requires на [] больше не будет автоматически требовать модуль Kernel. Аналогичным образом, установка :macros больше не будет автоматически импортировать макросы Kernel, такие как Kernel.if/2, Kernel.SpecialForms.case/2 и так далее.
Возвращает кортеж вида {value, binding}, где value — значение, возвращенное при оценке string. Если при оценке string произойдет ошибка, будет возбуждено исключение.
binding — список ключевых слов со значением всех связей переменных после оценки string. Ключ связи обычно является атомом, но может быть кортежем для переменных, определенных в другом контексте.
Примеры
iex> Code.eval_string("a + b", [a: 1, b: 2], file: __ENV__.file, line: __ENV__.line)
{3, [a: 1, b: 2]}
iex> Code.eval_string("c = a + b", [a: 1, b: 2], __ENV__)
{3, [a: 1, b: 2, c: 3]}
iex> Code.eval_string("a = a + b", [a: 1, b: 2])
{3, [a: 3, b: 2]} Для удобства вы можете передать __ENV__/0 в качестве аргумента opts и все импорты, требования и псевдонимы, определенные в текущей среде, будут автоматически перенесены:
iex> Code.eval_string("a + b", [a: 1, b: 2], __ENV__)
{3, [a: 1, b: 2]} fetch_docs(module_or_path)
Спецификации
fetch_docs(module() | String.t()) ::
{:docs_v1, annotation, beam_language, format, module_doc :: doc_content,
metadata, docs :: [doc_element]}
| {:error, :module_not_found | :chunk_not_found | {:invalid_chunk, binary()}}
when annotation: :erl_anno.anno(),
beam_language: :elixir | :erlang | :lfe | :alpaca | atom(),
doc_content: %{required(binary()) => binary()} | :none | :hidden,
doc_element:
{{kind :: atom(), function_name :: atom(), arity()}, annotation,
signature, doc_content, metadata},
format: binary(),
signature: [binary()],
metadata: map() Возвращает документацию для указанного модуля или пути к файлу .beam.
При указании имени модуля, он находит его код BEAM и считывает документацию из него.
При указании пути к файлу .beam он загрузит документацию непосредственно из этого файла.
Он возвращает терм, хранящийся в блоке документации в формате, определенном в EEP 48 или {:error, reason} если блок недоступен.
Примеры
# Module documentation of an existing module
iex> {:docs_v1, _, :elixir, _, %{"en" => module_doc}, _, _} = Code.fetch_docs(Atom)
iex> module_doc |> String.split("\n") |> Enum.at(0)
"Convenience functions for working with atoms."
# A module that doesn't exist
iex> Code.fetch_docs(ModuleNotGood)
{:error, :module_not_found} format_file!(file, opts \\ [])
Спецификации
format_file!(binary(), keyword()) :: iodata()
Форматирует файл.
См. format_string!/2 для получения дополнительной информации о форматировании кода и доступных параметрах.
format_string!(string, opts \\ [])
Характеристики
format_string!(binary(), keyword()) :: iodata()
Форматирует предоставленный код string.
Форматировщик получает строку, представляющую код Elixir, и возвращает iodata, представляющую отформатированный код в соответствии с предварительно определёнными правилами.
Параметры
-
:file— файл, содержащий строку, используемый для сообщения об ошибках -
:line— строка, с которой начинается строка, используемый для сообщения об ошибках -
:line_length— длина строки, к которой следует стремиться при форматировании документа. По умолчанию 98. Обратите внимание, что это значение используется в качестве ссылки, но оно не навязывается форматировщиком, так как иногда требуется вмешательство пользователя. См. раздел «Запуск форматировщика» -
:locals_without_parens— список ключевых слов пар имя-арность, которые должны сохраняться без скобок, когда это возможно. Арность может быть атомом:*, что подразумевает все арности данного имени. Форматировщик уже включает список функций, и этот параметр дополняет этот список. -
:rename_deprecated_at— переименовать все известные устаревшие функции в указанной версии в их не устаревшие аналоги. Ожидает корректнуюVersion, которая обычно является минимальной версией Elixir, поддерживаемой проектом. -
:force_do_end_blocks(с версии v1.9.0) — приtrue, преобразует все встроенные использованияdo: ...,else: ...и аналогичных элементов в блокиdo/end. По умолчаниюfalse. Обратите внимание, что этот параметр является конвергентным: после того, как вы установите его вtrue, все ключевые слова будут преобразованы. Если вы установите его вfalseпозже, блокиdo/endне будут преобразованы обратно в ключевые слова.
Принципы проектирования
Форматировщик был разработан в соответствии с тремя принципами.
Во-первых, форматировщик по умолчанию никогда не изменяет семантику кода. Это означает, что входной и выходной AST эквивалентны. Допускается необязательное поведение, такое как :rename_deprecated_at, которое может нарушить это гарантию.
Второй принцип заключается в предоставлении минимального количества настроек. Это облегчает принятие форматировщика, устраняя спорные моменты и гарантируя, что сообщество в целом придерживается единого стиля.
Форматировщик не жёстко кодирует имена. Форматировщик не будет вести себя специфичным образом, потому что функция названа defmodule, def, и т.д. Этот принцип отражает цель Elixir быть расширяемым языком, где разработчики могут расширять язык новыми конструкциями так, как будто они являются частью языка. Когда абсолютно необходимо изменить поведение на основе имени, это поведение должно быть настраиваемым, например, параметр :locals_without_parens.
Запуск форматировщика
Форматировщик пытается разместить как можно больше на одной строке и вставляет разрывы строк, где это возможно, когда это невозможно.
В некоторых случаях это может привести к нежелательному форматированию. Поэтому некоторый код, сгенерированный форматировщиком, может не иметь эстетического вида и может потребовать явного вмешательства разработчика. Вот почему мы не рекомендуем запускать форматировщик слепо в существующей базе кода. Вместо этого вы должны отформатировать и проверить каждый отформатированный файл.
Рассмотрим несколько примеров. Код ниже:
"this is a very long string ... #{inspect(some_value)}" может быть отформатирован как:
"this is a very long string ... #{
inspect(some_value)
}" Это происходит потому, что единственное место, где форматировщик может вставить новую строку без изменения семантики кода, находится в интерполяции. В таких сценариях мы рекомендуем разработчикам непосредственно корректировать код. Здесь мы можем использовать оператор бинарного конкатенации <>/2:
"this is a very long string " <>
"... #{inspect(some_value)}" Конкатенация строк позволяет коду поместиться на одной строке и также даёт больше возможностей форматировщику.
Аналогичный пример — когда форматировщик разбивает определение функции на несколько пунктов:
def my_function(
%User{name: name, age: age, ...},
arg1,
arg2
) do
...
end Хотя приведенный выше код полностью корректен, вы можете предпочесть сопоставить переменные структуры внутри тела функции, чтобы сохранить определение на одной строке:
def my_function(%User{} = user, arg1, arg2) do
%{name: name, age: age, ...} = user
...
end В некоторых ситуациях вы можете использовать тот факт, что форматировщик не генерирует элегантный код, как подсказку для рефакторинга. Рассмотрим этот код:
def board?(board_id, %User{} = user, available_permissions, required_permissions) do
Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
required_permissions == Enum.to_list(MapSet.intersection(MapSet.new(required_permissions), MapSet.new(available_permissions)))
end У приведенного выше кода очень длинные строки, и запуск форматировщика не решит эту проблему. На самом деле, форматировщик может сделать более очевидным, что у вас есть сложные выражения:
def board?(board_id, %User{} = user, available_permissions, required_permissions) do
Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
required_permissions ==
Enum.to_list(
MapSet.intersection(
MapSet.new(required_permissions),
MapSet.new(available_permissions)
)
)
end Рассматривайте такие случаи как подсказку, что ваш код следует рефакторить:
def board?(board_id, %User{} = user, available_permissions, required_permissions) do
Tracker.OrganizationMembers.user_in_organization?(user.id, board.organization_id) and
matching_permissions?(required_permissions, available_permissions)
end
defp matching_permissions?(required_permissions, available_permissions) do
intersection =
required_permissions
|> MapSet.new()
|> MapSet.intersection(MapSet.new(available_permissions))
|> Enum.to_list()
required_permissions == intersection
end Подводя итог: поскольку форматировщик не может изменить семантику вашего кода, иногда необходимо подкорректировать или рефакторить код, чтобы получить оптимальное форматирование. Для лучшего понимания того, как контролировать форматировщик, в следующих разделах мы опишем случаи, когда форматировщик сохраняет кодировку пользователя, и как контролировать многострочные выражения.
Сохранение форматирования пользователя
Форматировщик сохраняет входной формат в некоторых случаях. Они перечислены ниже:
-
Незначительные цифры в числах сохраняются как есть. Однако форматировщик всегда вставляет нижние подчеркивания для десятичных чисел с более чем 5 цифрами и преобразует шестнадцатеричные цифры в верхний регистр
-
Строки, списки символов, атомы и сигилы сохраняются как есть. Ни один символ не экранируется или экранируется автоматически. Выбор разделителя также сохраняется из входных данных
-
Новые строки внутри блоков сохраняются как во входных данных, за исключением: 1) выражения, занимающие несколько строк, всегда будут иметь пустую строку перед и после, и 2) пустые строки всегда объединяются в одну пустую строку
-
Выбор между
:doключевым словом и блокамиdo/endоставляется пользователю -
Списки, кортежи, битовые строки, карты, структуры и вызовы функций будут разбиты на несколько строк, если после открывающей скобки следует новая строка, а перед закрывающей скобки — новая строка
-
Новые строки перед определенными операторами (такими как операторы канала) и перед другими операторами (такими как операторы сравнения)
Вышеупомянутое поведение не гарантируется. Мы можем удалить или добавить новые правила в будущем. Цель документирования — предоставить лучшее понимание ожидаемого от форматировщика.
Многострочные списки, карты, кортежи и т.д.
Вы можете принудительно сделать так, чтобы списки, кортежи, битовые строки, карты, структуры и вызовы функций имели по одному элементу на строке, добавив новую строку после открывающей скобки и новую строку перед закрывающей скобки. Например:
[ foo, bar ]
Если вокруг скобок нет новых строк, форматировщик попытается поместить всё на одной строке, так что фрагмент ниже
[foo, bar]
будет отформатирован как
[foo, bar]
Вы также можете принудительно сделать вызовы функций и ключевые слова многострочными, поместив каждый элемент на отдельную строку:
defstruct name: nil,
age: 0 Форматировщик сохранит приведенный выше код с одним элементом ключевого слова на строке. Чтобы этого избежать, просто всё поместите на одной строке.
Скобки и отсутствие скобок в вызовах функций
Elixir имеет два синтаксиса для вызовов функций: со скобками и без. По умолчанию Elixir добавляет скобки ко всем вызовам, за исключением:
- вызовов с блоками do/end
- локальных вызовов без скобок, где имя и арность локального вызова также указаны в
:locals_without_parens(за исключением вызовов с арностью 0, где компилятор всегда требует скобки)
Выбор скобок и отсутствие скобок также влияет на отступы. Когда вызов функции со скобками не помещается в одну строку, форматировщик добавляет новую строку вокруг скобок и отступает аргументы на две позиции:
some_call( arg1, arg2, arg3 )
С другой стороны, вызовы функций без скобок всегда отступаются на длину самого вызова функции, как в этом примере:
some_call arg1,
arg2,
arg3 Если последним аргументом является структура данных, такая как карта или список, и начало структуры данных помещается в одну строку с вызовом функции, то отступы не происходят, что позволяет использовать код такого вида:
Enum.reduce(some_collection, initial_value, fn element, acc ->
# code
end)
some_function_without_parens %{
foo: :bar,
baz: :bat
} Комментарии к коду
Форматировщик также обрабатывает комментарии к коду таким образом, чтобы гарантировать, что пробел всегда добавляется между началом комментария (#) и следующим символом.
Форматировщик также выносит все хвостовые комментарии на предыдущую строку. Например, приведенный ниже код
hello #world
будет переписан как
# world hello
Поскольку комментарии к коду обрабатываются отдельно от представления кода (AST), есть ситуации, когда форматировщик кода считает комментарии неоднозначными. Например, комментарий в анонимной функции ниже
fn
arg1 ->
body1
# comment
arg2 ->
body2
end и в этом
fn
arg1 ->
body1
# comment
arg2 ->
body2
end считаются эквивалентными (вложенность отбрасывается вместе с большей частью форматирования пользователя). В таких случаях форматировщик кода всегда будет форматировать во втором варианте.
get_docs(module, kind)
Характеристики
get_docs(module(), :moduledoc | :docs | :callback_docs | :type_docs | :all) :: nil
Устаревшая функция для получения устаревшего формата документации.
Elixir v1.7 использует EEP 48, который представляет собой новый формат документации, предназначенный для совместного использования во всех языках BEAM. Старый формат, используемый функцией Code.get_docs/2, больше недоступен, и поэтому эта функция всегда возвращает nil. Используйте Code.fetch_docs/1 вместо этого.
prepend_path(path)
Характеристики
prepend_path(Path.t()) :: true | {:error, :bad_directory} Добавляет путь в начало списка путей кода виртуальной машины Erlang.
Это список каталогов, которые виртуальная машина Erlang использует для поиска кода модулей.
Путь расширяется с помощью Path.expand/1 перед добавлением в начало. Если такого пути не существует, возвращается ошибка.
Примеры
Code.prepend_path(".")
#=> true
Code.prepend_path("/does_not_exist")
#=> {:error, :bad_directory} purge_compiler_modules()
Характеристики
purge_compiler_modules() :: {:ok, non_neg_integer()} Очистить модули компилятора.
Компилятор использует временные модули для компиляции кода. Например, elixir_compiler_1, elixir_compiler_2, и т. д. В случае, если скомпилированный код хранит ссылки на анонимные функции или подобное, компилятор Elixir может не смочь вернуть эти модули, оставляя ненужное количество кода в памяти и, в конечном итоге, приводя к таким модулям, как elixir_compiler_12345.
Эта функция очищает все модули, в настоящее время хранящиеся компилятором, позволяя повторно использовать старые имена модулей компилятора. Если какие-либо процессы выполняют код из таких модулей, они также будут завершены.
Возвращает {:ok, number_of_modules_purged}.
require_file(file, relative_to \\ nil)
Характеристики
require_file(binary(), nil | binary()) :: [{module(), binary()}] | nil Загрузить указанный file.
Принимает relative_to в качестве аргумента, чтобы указать, где находится файл. Если файл уже был загружен, require_file/2 ничего не делает и возвращает nil.
Обратите внимание, что если require_file/2 вызывается разными процессами одновременно, первый вызвавший процесс require_file/2 приобретает блокировку, а остальные будут блокироваться, пока файл не станет доступным. Это означает, что если require_file/2 вызывается более одного раза с заданным файлом, этот файл будет скомпилирован только один раз. Первый вызвавший require_file/2 процесс получит список загруженных модулей, остальные получат nil.
См. compile_file/2, если вы хотите скомпилировать файл без отслеживания его имён файлов. И, наконец, если вы хотите получить результат вычисления файла, а не модулей, определённых в нём, см. eval_file/2.
Примеры
Если файл ещё не загружен, возвращает список модулей:
modules = Code.require_file("eex_test.exs", "../eex/test")
List.first(modules)
#=> {EExTest.Compiled, <<70, 79, 82, 49, ...>>} Если файл уже загружен, возвращает nil:
Code.require_file("eex_test.exs", "../eex/test")
#=> nil required_files()
Характеристики
required_files() :: [binary()]
Список всех загруженных файлов.
Примеры
Code.require_file("../eex/test/eex_test.exs")
List.first(Code.required_files()) =~ "eex_test.exs"
#=> true string_to_quoted(string, opts \\ [])
Характеристики
string_to_quoted(List.Chars.t(), keyword()) ::
{:ok, Macro.t()} | {:error, {line :: pos_integer(), term(), term()}} Преобразует заданную строку в её строковое представление.
Возвращает {:ok, quoted_form} при успехе, {:error, {line, error, token}} в противном случае.
Параметры
-
:file- имя файла, которое будет сообщено в случае ошибок разбора. По умолчанию "nofile". -
:line- начальная строка анализируемой строки. По умолчанию 1. -
:columns- когдаtrue, добавить ключ:columnк метаданным в кавычках. По умолчаниюfalse. -
:existing_atoms_only- когдаtrue, вызывать ошибку, если встречаются несуществующие атомы, найденные токенизатором. По умолчаниюfalse. -
:static_atom_encoder- функция кодирования статических атомов, см. раздел "Функция:static_atom_encoder" ниже. Этот параметр переопределяет поведение:existing_atoms_onlyдля статических атомов, но:existing_atoms_onlyвсё ещё используется для динамических атомов, таких как атомы с интерполяциями. -
:warn_on_unnecessary_quotes- когдаfalse, не предупреждать, когда атомы, ключевые слова или вызовы имеют ненужные кавычки. По умолчаниюtrue.
Macro.to_string/2
Обратная операция преобразования строки в её кавыченное представление — это Macro.to_string/2, которая преобразует кавыченное представление в строковое/двоичное представление.
Функция :static_atom_encoder
Когда static_atom_encoder: &my_encoder/2 передаётся в качестве аргумента, my_encoder/2 вызывается каждый раз, когда токенизатор нуждается в создании "статического" атома. Статические атомы — это атомы в AST, которые функционируют как псевдонимы, удалённые вызовы, локальные вызовы, имена переменных, обычные атомы и списки ключевых слов.
Функция-кодировщик получит имя атома (как двоичную строку) и список ключевых слов с текущим файлом, строкой и столбцом. Она должна вернуть {:ok, token :: term} | {:error, reason :: binary}.
Функция-кодировщик должна создать атом из заданной строки. Она должна вернуть либо {:ok, term}, где term — это атом. Возможным является возвращение чего-то иного, чем атом, однако в этом случае AST больше не является "валидным", так как его нельзя использовать для компиляции или оценки кода Elixir. Пример применения — если вы хотите использовать парсер Elixir в пользовательском интерфейсе, но не хотите исчерпать таблицу атомов.
Функция кодирования атомов не вызывается для всех атомов, которые присутствуют в AST. Она не будет вызвана для следующих атомов:
-
операторов (
:+,:-, и так далее) -
ключевых слов синтаксиса (
fn,do,else, и так далее) -
атомов, содержащих интерполяцию (
:"#{1 + 1} is two"), так как эти атомы создаются во время выполнения.
string_to_quoted!(string, opts \\ [])
Характеристики
string_to_quoted!(List.Chars.t(), keyword()) :: Macro.t()
Преобразует заданную строку в её кавычное представление.
Возвращает ast при успехе, в противном случае генерирует исключение. Исключение — TokenMissingError в случае отсутствия токена (обычно из-за неполного выражения), SyntaxError в противном случае.
См. string_to_quoted/2 для информации о параметрах.
unrequire_files(files)
Характеристики
unrequire_files([binary()]) :: :ok
Удаляет файлы из списка загруженных файлов.
Модули, определённые в файле, не удаляются; вызов этой функции только удаляет их из списка, позволяя загрузить их снова.
Примеры
# Require EEx test code
Code.require_file("../eex/test/eex_test.exs")
# Now unrequire all files
Code.unrequire_files(Code.required_files())
# Notice modules are still available
function_exported?(EExTest.Compiled, :before_compile, 0)
#=> true
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.9.4/Code.html