Source Правила именования
Этот документ содержит описание правил именования в Elixir, от регистров символов до знаков препинания.
Правило именования, по определению, является подмножеством синтаксиса Elixir. Правило призвано следовать и устанавливать лучшие практики для языка и сообщества. Если вам нужна полная справка по синтаксису Elixir, а не только по правилам, см. справочник по синтаксису.
Регистр символов
Разработчики Elixir должны использовать snake_case при определении переменных, имён функций, атрибутов модулей и т.п.:
some_map = %{this_is_a_key: "and a value"}
is_map(some_map)
Исключение составляют псевдонимы, обычно используемые в качестве имён модулей, которые должны быть прописными и написаны в CamelCase, например OptionParser. Для псевдонимов заглавные буквы сохраняются в аббревиатурах, таких как ExUnit.CaptureIO или Mix.SCM.
Атомы могут быть написаны либо в :snake_case или :CamelCase, хотя по соглашению используется вариант с нижним подчёркиванием в Elixir.
Как правило, имена файлов следуют snake_case соглашению модуля, который они определяют. Например, MyApp должен быть определён внутри файла my_app.ex. Однако это всего лишь соглашение. В конечном счёте, можно использовать любое имя файла, так как они никак не влияют на скомпилированный код.
Подчёркивание (_foo)
Elixir использует подчёркивания в разных ситуациях.
Например, значение, которое не должно использоваться, должно быть назначено _ или переменной, начинающейся с подчёркивания:
iex> {:ok, _contents} = File.read("README.md")
Имена функций также могут начинаться с подчёркивания. Такие функции никогда не импортируются по умолчанию:
iex> defmodule Example do ...> def _wont_be_imported do ...> :oops ...> end ...> end iex> import Example iex> _wont_be_imported() ** (CompileError) iex:1: undefined function _wont_be_imported/0
Благодаря этому свойству Elixir использует функции, начинающиеся с подчёркивания, для прикрепления метаданных времени компиляции к модулям. Такие функции чаще всего имеют формат __foo__. Например, каждый модуль в Elixir имеет функцию __info__/1:
iex> String.__info__(:functions) [at: 2, capitalize: 1, chunk: 2, ...]
Elixir также включает пять специальных форм, которые следуют формату с двойным подчёркиванием: __CALLER__/0, __DIR__/0, __ENV__/0 и __MODULE__/0 извлекают информацию о текущей среде времени компиляции, а __STACKTRACE__/0 извлекает стек-трейс для текущего исключения.
Заключительный восклицательный знак (foo!)
Заключительный восклицательный знак указывает на функцию или макрос, где случаи ошибки генерируют исключение. Они чаще всего существуют в качестве «варианта подъёма» функции, которая возвращает :ok/:error кортежи (или nil).
Один пример — File.read/1 и File.read!/1. File.read/1 вернёт кортеж успеха или неудачи, а File.read!/1 вернёт простое значение или же сгенерирует исключение:
iex> File.read("file.txt")
{:ok, "file contents"}
iex> File.read("no_such_file.txt")
{:error, :enoent}
iex> File.read!("file.txt")
"file contents"
iex> File.read!("no_such_file.txt")
** (File.Error) could not read file no_such_file.txt: no such file or directory
Предпочтительнее использовать версию без !, если вы хотите обработать разные результаты с помощью сопоставления с образцом:
case File.read(file) do
{:ok, body} -> # do something with the `body`
{:error, reason} -> # handle the error caused by `reason`
end
Однако, если вы ожидаете, что результат всегда будет успешным (например, если вы ожидаете, что файл всегда существует), вариант с восклицательным знаком может быть удобнее и выведет более полезное сообщение об ошибке (чем неудачное сопоставление с образцом) в случае неудачи.
При рассмотрении случаев ошибок мы часто думаем о семантических ошибках, связанных с выполняемой операцией, например, неудачной попытке открыть файл или попытке извлечь ключ из карты. Ошибки, возникающие из-за неверного типа аргумента или аналогичных причин, всегда должны генерировать исключение независимо от наличия восклицательного знака в имени функции. В таких случаях исключение обычно представляет собой ArgumentError или подробное FunctionClauseError:
iex(1)> File.read(123)
** (FunctionClauseError) no function clause matching in IO.chardata_to_string/1
The following arguments were given to IO.chardata_to_string/1:
# 1
123
Attempted function clauses (showing 2 out of 2):
def chardata_to_string(string) when is_binary(string)
def chardata_to_string(list) when is_list(list)
Другие примеры пар функций: Base.decode16/2 и Base.decode16!/2, File.cwd/0 и File.cwd!/0. В некоторых случаях могут быть функции с восклицательным знаком без аналога без него. Они также подразумевают возможность ошибок, например: Protocol.assert_protocol!/1 и PartitionSupervisor.resize!/2. Это может быть полезно, если в будущем вы планируете добавить вариант без подъёма исключения.
Заключительный вопросительный знак (foo?)
Функции, возвращающие логическое значение, имеют имя с заключительным вопросительным знаком.
Примеры: Keyword.keyword?/1, Mix.debug?/0, String.contains?/2
Однако функции, возвращающие булевые значения и допустимые в условиях, следуют другому соглашению, описанному ниже.
Префикс is_ (is_foo)
Проверки типов и другие проверки логических значений, разрешённые в условиях, имеют префикс is_.
Примеры: Integer.is_even/1, is_list/1
Эти функции и макросы следуют соглашению Erlang с префиксом is_, а не с заключительным вопросительным знаком, именно для того, чтобы указать, что они допускаются в условиях.
Обратите внимание, что проверки типов, которые недопустимы в условиях, не следуют этому соглашению. Например: Keyword.keyword?/1.
Специальные имена
Некоторые имена имеют специфическое значение в Elixir. Мы приводим эти случаи ниже.
длина и размер
Когда вы видите size в имени функции, это означает, что операция выполняется за постоянное время (также записывается как «O(1) время»), поскольку размер хранится вместе со структурой данных.
Примеры: map_size/1, tuple_size/1
Когда вы видите length, операция выполняется за линейное время («O(n) время»), поскольку вся структура данных должна быть пройдена.
Примеры: length/1, String.length/1
Другими словами, функции, использующие слово «размер» в своём имени, будут выполняться за одинаковое время, независимо от того, мала или велика структура данных. Напротив, функции, использующие «длина» в своём имени, будут выполняться дольше по мере увеличения размера структуры данных.
получить, извлечь, извлечь!
Когда вы видите функции get, fetch, и fetch! для структур данных ключ-значение, вы можете ожидать следующего поведения:
-
getвозвращает значение по умолчанию (которое по умолчанию равноnil), если ключ отсутствует, или возвращает запрошенное значение. -
fetchвозвращает:errorесли ключ отсутствует, или возвращает{:ok, value}если он присутствует. -
fetch!генерирует исключение, если ключ отсутствует, или возвращает запрошенное значение.
Примеры: Map.get/2, Map.fetch/2, Map.fetch!/2, Keyword.get/2, Keyword.fetch/2, Keyword.fetch!/2
сравнить
Функция compare/2 должна возвращать :lt если первый терм меньше второго, :eq если два терма сравниваются как эквивалентные, или :gt если первый терм больше второго.
Примеры: DateTime.compare/2
Обратите внимание, что это конкретное соглашение важно из-за ожиданий Enum.sort/2
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/naming-conventions.html