Spec-Zone.ru › Elixir 1.7

Ядро

Kernel является стандартной средой Elixir.

В основном оно состоит из:

  • базовых языковых примитивов, таких как арифметические операторы, создание процессов, обработка типов данных и т. д
  • макросов для управления потоком и определения новой функциональности (модулей, функций и так далее)
  • проверок для расширения шаблонов сопоставления

Вы можете использовать функции/макросы Kernel без префикса Kernel в любом коде Elixir, так как все его функции и макросы автоматически импортируются. Например, в IEx:

iex> is_number(13)
true

Если вы не хотите импортировать функцию или макрос из Kernel, используйте опцию :except и затем перечислите функцию/макрос по арности:

import Kernel, except: [if: 2, unless: 2]

См. Kernel.SpecialForms.import/2 для получения дополнительной информации об импорте.

В Elixir также есть специальные формы, которые всегда импортируются и не могут быть пропущены. Они описаны в Kernel.SpecialForms.

Стандартная библиотека

Kernel предоставляет базовые возможности, на которых построена стандартная библиотека Elixir. Рекомендуется изучить стандартную библиотеку для получения расширенной функциональности. Вот основные группы модулей в стандартной библиотеке (этот список не является полным справочником, см. боковую панель документации для всех записей).

Встроенные типы

Следующие модули обрабатывают встроенные типы данных Elixir:

  • Atom - литеральные константы с именем (true, false, и nil являются атомами)
  • Float - числа с плавающей точкой
  • Function - ссылка на фрагмент кода, созданный с помощью специальной формы Kernel.SpecialForms.fn/2
  • Integer - целые числа (не дроби)
  • List - коллекции переменного числа элементов (связанные списки)
  • Map - коллекции пар ключ-значение
  • Process - лёгкие потоки выполнения
  • Port - механизмы взаимодействия с внешним миром
  • Tuple - коллекции фиксированного числа элементов

Существует два типа данных без сопровождающего модуля:

  • Битовая строка - последовательность битов, созданная с помощью Kernel.SpecialForms.<<>>/1. Когда количество битов кратно 8, они называются двоичными и могут быть обработаны модулем Erlang’s :binary
  • Ссылка - уникальное значение в системе выполнения, созданное с помощью make_ref/0

Типы данных

Elixir также предоставляет другие типы данных, построенные на основе перечисленных выше типов. Некоторые из них:

  • Date - структуры year-month-day в заданном календаре
  • DateTime - дата и время с часовым поясом в заданном календаре
  • Exception - данные, поднятые при ошибках и непредвиденных сценариях
  • MapSet - неупорядоченные коллекции уникальных элементов
  • NaiveDateTime - дата и время без часового пояса в заданном календаре
  • Keyword - списки кортежей из двух элементов, часто представляющих необязательные значения
  • Range - включительные диапазоны между двумя целыми числами
  • Regex - регулярные выражения
  • String - двоичные данные, закодированные в UTF-8, представляющие символы
  • Time - структуры hour:minute:second в заданном календаре
  • URI - представление URI, идентифицирующих ресурсы
  • Version - представление версий и требований

Модули системы

Модули, взаимодействующие с базовой системой, такие как:

  • IO - обрабатывает ввод и вывод
  • File - взаимодействует с файловой системой
  • Path - управляет путями к файлам
  • System - считывает и записывает системную информацию

Протоколы

Протоколы добавляют полиморфный диспетчер в Elixir. Они являются контрактами, реализуемыми типами данных. См. defprotocol/2 для получения дополнительной информации о протоколах. Стандартная библиотека Elixir предоставляет следующие протоколы:

  • Collectable - собирает данные в тип данных
  • Enumerable - обрабатывает коллекции в Elixir. Модуль Enum предоставляет жадные функции для работы с коллекциями, модуль Stream предоставляет ленивые функции
  • Inspect - преобразует типы данных в их представление в языке программирования
  • List.Chars - преобразует типы данных в их представление во внешнем мире как списки символов (не связанные с программированием)
  • String.Chars - преобразует типы данных в их представление во внешнем мире как строки (не связанные с программированием)

Функциональность, ориентированная на процессы и приложения

Следующие модули строятся на основе процессов, чтобы обеспечить конкурентность, отказоустойчивость и многое другое.

  • Agent - процесс, который инкапсулирует изменяемое состояние
  • Application - функции для запуска, остановки и настройки приложений
  • GenServer - общий API клиент-сервер
  • Registry - хранилище ключ-значение на основе процессов
  • Supervisor - процесс, который отвечает за запуск, контроль и остановку других процессов
  • Task - процесс, который выполняет вычисления
  • Task.Supervisor - супервайзер для управления задачами исключительно

Дополнительные документы

Документация Elixir также включает дополнительные документы в разделе «Страницы». Это:

  • Совместимость и устаревшие функции - список совместимости между каждой версией Elixir и Erlang/OTP, схема выпуска; список всех устаревших функций, когда они устарели и альтернативы
  • Проверки - список всех доступных проверок и расширений
  • Руководства по библиотекам - общие рекомендации, антипаттерны и правила для разработчиков библиотек
  • Конвенции именования - правила именования для кода Elixir
  • Операторы - список всех операторов Elixir и их приоритет
  • Справочник по синтаксису - справочник по синтаксису языка
  • Типоспецификации- типы и спецификации функций, включая список типов
  • Синтаксис Unicode - описание поддержки Unicode в Elixir
  • Написание документации - рекомендации по написанию документации в Elixir.

Инкапсуляция

Некоторые функции, описанные в этом модуле, инкапсулируются компилятором Elixir в их эквиваленты Erlang в :erlang модуле. Эти функции называются BIF (встроенными внутренними функциями) в Erlang, и они демонстрируют интересные свойства, так как некоторые из них разрешены в проверках, а другие используются для оптимизации компилятора.

Большинство инкапсулированных функций можно увидеть при отслеживании функции:

iex> &Kernel.is_atom/1
&:erlang.is_atom/1

Эти функции будут явно помечены в документации как «инкапсулированные компилятором».

Сводка

Функции

!value

Логическое отрицание

left != right

Возвращает true если два элемента не равны

left !== right

Возвращает true если два элемента не точно равны

left && right

Предоставляет оператор короткого замыкания, который вычисляет и возвращает второе выражение только в том случае, если первое выражение вычисляется как true (т.е., это ни nil, ни false). В противном случае возвращает первое выражение.

left * right

Арифметическое умножение

left ++ right

Конкатенирует правильный список и термин, возвращая список

+value

Арифметический унарный плюс

left + right

Арифметическое сложение

left -- right

Удаляет первое вхождение элемента из левого списка для каждого элемента в правом

-value

Арифметический унарный минус

left - right

Арифметическое вычитание

first..last

Возвращает диапазон со значениями указанных first и last целых чисел

left / right

Арифметическое деление

left < right

Возвращает true если слева меньше, чем справа

left <= right

Возвращает true если слева меньше или равно, чем справа

left <> right

Конкатенирует два бинарных значения

left == right

Возвращает true если два элемента равны

left === right

Возвращает true если два элемента точно равны

left =~ right

Сопоставляет термин слева с регулярным выражением или строкой справа

left > right

Возвращает true если слева больше, чем справа

left >= right

Возвращает true если слева больше или равно, чем справа

@expr

Читает и записывает атрибуты текущего модуля

abs(number)

Возвращает целое или вещественное число, которое является абсолютным значением number

alias!(alias)

При использовании внутри цитирования, отмечает, что данный псевдоним не должен быть санизирован. Это означает, что псевдоним будет расширен при расширении макроса.

left and right

Логическое И

apply(fun, args)

Вызывает заданную анонимную функцию fun со списком аргументов args

apply(module, function_name, args)

Вызывает заданную функцию из module со списком аргументов args

binary_part(binary, start, length)

Извлекает часть бинарного значения, начиная с start длиной length. Бинарные значения индексируются с нуля

binding(context \\ nil)

Возвращает связывание для данного контекста в виде списка ключевых слов

bit_size(bitstring)

Возвращает целое число, которое является размером в битах bitstring

byte_size(bitstring)

Возвращает количество байтов, необходимых для хранения bitstring

def(call, expr \\ nil)

Определяет функцию с заданным именем и телом

defdelegate(funs, opts)

Определяет функцию, которая делегирует вызов другому модулю

defexception(fields)

Определяет исключение

defguard(guard)

Генерирует макрос, подходящий для использования в выражениях условий

defguardp(guard)

Генерирует частный макрос, подходящий для использования в выражениях условий

defimpl(name, opts, do_block \\ [])

Определяет реализацию для данного протокола

defmacro(call, expr \\ nil)

Определяет макрос с заданным именем и телом

defmacrop(call, expr \\ nil)

Определяет частный макрос с заданным именем и телом

defmodule(alias, do_block)

Определяет модуль с заданным именем и содержимым

defoverridable(keywords_or_behaviour)

Делает указанные функции в текущем модуле переопределяемыми

defp(call, expr \\ nil)

Определяет частную функцию с заданным именем и телом

defprotocol(name, do_block)

Определяет протокол

defstruct(fields)

Определяет структуру

destructure(left, right)

Деструктурирует два списка, присваивая каждый термин в правом списке соответствующему термину в левом

div(dividend, divisor)

Выполняет целочисленное деление

elem(tuple, index)

Возвращает элемент с нулевой базой index в tuple

exit(reason)

Останавливает выполнение вызывающего процесса с указанной причиной

function_exported?(module, function, arity)

Возвращает true если module загружен и содержит публичную function с заданным arity, в противном случае false

get_and_update_in(path, fun)

Получает значение и обновляет вложенную структуру данных с помощью заданной path

get_and_update_in(data, keys, fun)

Получает значение и обновляет вложенную структуру

get_in(data, keys)

Получает значение из вложенной структуры

hd(list)

Возвращает голову списка. Вызывает ArgumentError если список пуст

if(condition, clauses)

Предоставляет макрос if/2

left in right

Проверяет, является ли элемент слева элементом коллекции справа

inspect(term, opts \\ [])

Проверяет заданный аргумент в соответствии с протоколом Inspect. Второй аргумент - список ключевых слов с параметрами для управления проверкой

is_atom(term)

Возвращает true если term является атомом; в противном случае возвращает false

is_binary(term)

Возвращает true если term является бинарным; в противном случае возвращает false

is_bitstring(term)

Возвращает true если term является бистроковой (включая бинарное); в противном случае возвращает false

is_boolean(term)

Возвращает true если term это либо атом true, либо атом false (т.е., булево); в противном случае возвращает false

is_float(term)

Возвращает true если term — число с плавающей точкой; в противном случае возвращает false

is_function(term)

Возвращает true если term — функция; в противном случае возвращает false

is_function(term, arity)

Возвращает true если term — функция, которая может быть применена с arity количеством аргументов; в противном случае возвращает false

is_integer(term)

Возвращает true если term — целое число; в противном случае возвращает false

is_list(term)

Возвращает true если term — список с нулем или более элементами; в противном случае возвращает false

is_map(term)

Возвращает true если term — карта; в противном случае возвращает false

is_nil(term)

Возвращает true если term — nil, false в противном случае

is_number(term)

Возвращает true если term — целое или число с плавающей точкой; в противном случае возвращает false

is_pid(term)

Возвращает true если term — PID (идентификатор процесса); в противном случае возвращает false

is_port(term)

Возвращает true если term — идентификатор порта; в противном случае возвращает false

is_reference(term)

Возвращает true если term — ссылка; в противном случае возвращает false

is_tuple(term)

Возвращает true если term — кортеж; в противном случае возвращает false

length(list)

Возвращает длину list

macro_exported?(module, macro, arity)

Возвращает true если module загружен и содержит публичную macro с заданным arity, в противном случае false

make_ref()

Возвращает почти уникальную ссылку

map_size(map)

Возвращает размер карты

match?(pattern, expr)

Удобный макрос, который проверяет, соответствует ли правая часть (выражение) левой части (шаблону)

max(first, second)

Возвращает наибольший из двух заданных терминов в соответствии с порядком Erlang

min(first, second)

Возвращает наименьший из двух заданных терминов в соответствии с порядком Erlang

node()

Возвращает атом, представляющий имя локального узла. Если узел не активен, возвращается :nonode@nohost вместо этого

node(arg)

Возвращает узел, где находится данный аргумент. Аргумент может быть PID, ссылкой или портом. Если локальный узел не активен, возвращается :nonode@nohost

not(value)

Логическое отрицание

left or right

Логическое ИЛИ

pop_in(path)

Удаляет ключ из вложенной структуры по заданному path

pop_in(data, keys)

Удаляет ключ из заданной вложенной структуры

put_elem(tuple, index, value)

Помещает value в заданную нулевую index в tuple

put_in(path, value)

Помещает значение в вложенную структуру по заданному path

put_in(data, keys, value)

Помещает значение в вложенную структуру

raise(message)

Вызывает исключение

raise(exception, attributes)

Вызывает исключение

rem(dividend, divisor)

Вычисляет остаток от деления целых чисел

reraise(message, stacktrace)

Вызывает исключение, сохраняя предыдущий стек вызовов

reraise(exception, attributes, stacktrace)

Вызывает исключение, сохраняя предыдущий стек вызовов

round(number)

Округляет число до ближайшего целого

self()

Возвращает PID (идентификатор процесса) вызывающего процесса

send(dest, message)

Отправляет сообщение заданному dest и возвращает сообщение

sigil_C(term, modifiers)

Обрабатывает сигил ~C для списков символов

sigil_D(date, modifiers)

Обрабатывает сигил ~D для дат

sigil_N(date, modifiers)

Обрабатывает сигил ~N для дат без учета часового пояса

sigil_R(term, modifiers)

Обрабатывает сигил ~R для регулярных выражений

sigil_S(term, modifiers)

Обрабатывает сигил ~S для строк

sigil_T(date, modifiers)

Обрабатывает сигил ~T для времени

sigil_W(term, modifiers)

Обрабатывает сигил ~W для списков слов

sigil_c(term, modifiers)

Обрабатывает сигил ~c для списков символов

sigil_r(term, modifiers)

Обрабатывает сигил ~r для регулярных выражений

sigil_s(term, modifiers)

Обрабатывает сигил ~s для строк

sigil_w(term, modifiers)

Обрабатывает сигил ~w для списков слов

spawn(fun)

Запускает заданную функцию и возвращает ее PID

spawn(module, fun, args)

Запускает заданную функцию fun из заданного module, передавая ей заданные args и возвращает ее PID

spawn_link(fun)

Запускает заданную функцию, связывает ее с текущим процессом и возвращает ее PID

spawn_link(module, fun, args)

Запускает заданную функцию fun из заданного module, передавая ей заданные args, связывает ее с текущим процессом и возвращает ее PID

spawn_monitor(fun)

Запускает заданную функцию, отслеживает ее и возвращает ее PID и ссылку на мониторинг

spawn_monitor(module, fun, args)

Запускает заданный модуль и функцию, передавая заданные аргументы, отслеживает его и возвращает его PID и ссылку на мониторинг

struct!(struct, fields \\ [])

Аналогично struct/2, но проверяет корректность ключей

struct(struct, fields \\ [])

Создает и обновляет структуры

throw(term)

Нелокальный возврат из функции

tl(list)

Возвращает хвост списка. Вызывает ArgumentError, если список пуст

END_OF_DOCUMENT_MARKER
to_charlist(term)

Преобразует данный термин в список символов в соответствии с протоколом List.Chars

to_string(term)

Преобразует аргумент в строку в соответствии с протоколом String.Chars

trunc(number)

Возвращает целую часть number

tuple_size(tuple)

Возвращает размер кортежа

unless(condition, clauses)

Обеспечивает макрос unless

update_in(path, fun)

Обновляет вложенную структуру с помощью указанной функции path

update_in(data, keys, fun)

Обновляет ключ во вложенной структуре

use(module, opts \\ [])

Использует указанный модуль в текущем контексте

var!(var, context \\ nil)

При использовании внутри цитирования отмечает, что данная переменная не должна подвергаться гигиенизации

left |> right

Оператор конвейера

left || right

Обеспечивает оператор короткого замыкания, оценивающий и возвращающий второе выражение только в том случае, если первое выражение не оценивается как true (т.е., оно либо nil или false). В противном случае возвращает первое выражение

Функции

!value (макрос)

Логическое отрицание.

Принимает любой аргумент (не только булевы значения) и возвращает true если аргумент false или nil; в противном случае возвращает false.

Запрещено в условиях.

Примеры

iex> !Enum.empty?([])
false

iex> !List.first([])
true

left != right

term() != term() :: boolean()

Возвращает true если два элемента не равны.

Этот оператор считает 1 и 1.0 равными. Для сравнения совпадений используйте !==.

Все термины в Elixir могут сравниваться друг с другом.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 != 2
true

iex> 1 != 1.0
false

left !== right

term() !== term() :: boolean()

Возвращает true если два элемента не равны точно.

Все термины в Elixir могут сравниваться друг с другом.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 !== 2
true

iex> 1 !== 1.0
true

left && right (макрос)

Обеспечивает оператор короткого замыкания, оценивающий и возвращающий второе выражение только в том случае, если первое выражение оценивается как true (т.е., оно ни nil ни false). В противном случае возвращает первое выражение.

Запрещено в условиях.

Примеры

iex> Enum.empty?([]) && Enum.empty?([])
true

iex> List.first([]) && true
nil

iex> Enum.empty?([]) && List.first([1])
1

iex> false && throw(:bad)
false

Обратите внимание, что, в отличие от and/2, этот оператор принимает любое выражение в качестве первого аргумента, а не только булевы значения.

left * right

float() * integer() :: float()
integer() * float() :: float()
float() * float() :: float()
integer() * integer() :: integer()

Арифметическое умножение.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 * 2
2

left ++ right

list() ++ term() :: maybe_improper_list()

Конкатенирует правильный список и термин, возвращая список.

Сложность a ++ b пропорциональна length(a), поэтому избегайте многократного добавления к спискам произвольной длины, например, list ++ [item]. Вместо этого рассмотрите добавление в начало с помощью [item | rest] и последующего разворота.

Если первый операнд не является правильным списком, он возвращает неправильный список. Если второй операнд не является правильным списком, он вызывает ArgumentError.

Встроен компилятором.

Примеры

iex> [1] ++ [2, 3]
[1, 2, 3]

iex> 'foo' ++ 'bar'
'foobar'

# returns an improper list
iex> [1] ++ 2
[1 | 2]

# returns a proper list
iex> [1] ++ [2]
[1, 2]

# improper list on the right will return an improper list
iex> [1] ++ [2 | 3]
[1, 2 | 3]

+value

+value :: value when value: number()

Арифметическое унарное плюс.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> +1
1

left + right

float() + integer() :: float()
integer() + float() :: float()
float() + float() :: float()
integer() + integer() :: integer()

Арифметическое сложение.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 + 2
3

left -- right

list() -- list() :: list()

Удаляет первое вхождение элемента слева для каждого элемента справа.

Сложность a -- b пропорциональна length(a) * length(b), что означает, что это будет очень медленно, если и a и b являются длинными списками. В таких случаях рассмотрите преобразование каждого списка в MapSet и использование MapSet.difference/2.

Встроен компилятором.

Примеры

iex> [1, 2, 3] -- [1, 2]
[3]

iex> [1, 2, 3, 2, 1] -- [1, 2, 2]
[3, 1]

-value

-float() :: float()
-neg_integer() :: pos_integer()
-pos_integer() :: neg_integer()
-0 :: 0

Арифметическое унарное минус.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> -2
-2

left - right

float() - integer() :: float()
integer() - float() :: float()
float() - float() :: float()
integer() - integer() :: integer()

Арифметическое вычитание.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 - 2
-1

first..last (макрос)

Возвращает диапазон с указанными first и last целыми числами.

Если last больше, чем first, диапазон будет возрастающим от first до last. Если first больше, чем last, диапазон будет убывающим от first до last. Если first равно last, диапазон будет содержать один элемент — само число.

Примеры

iex> 0 in 1..3
false

iex> 1 in 1..3
true

iex> 2 in 1..3
true

iex> 3 in 1..3
true

left / right

number() / number() :: float()

Арифметическое деление.

Результат всегда является числом с плавающей точкой. Используйте div/2 и rem/2, если вам нужно целочисленное деление или остаток.

Возбуждает ArithmeticError если right равно 0 или 0.0.

Разрешено в условиях. Встроен компилятором.

Примеры

1 / 2
#=> 0.5

-3.0 / 2.0
#=> -1.5

5 / 1
#=> 5.0

7 / 0
#=> ** (ArithmeticError) bad argument in arithmetic expression

left < right

term() < term() :: boolean()

Возвращает true если left меньше, чем right.

Все термины в Elixir могут сравниваться друг с другом.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 < 2
true

left <= right

term() <= term() :: boolean()

Возвращает true если left меньше или равно right.

Все термины в Elixir могут сравниваться друг с другом.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 <= 2
true

left <> right (макрос)

Конкатенирует две бинарные строки.

Примеры

iex> "foo" <> "bar"
"foobar"

Оператор <>/2 также может использоваться в сопоставлении с образцом (и в условиях), если левый аргумент является литеральной бинарной строкой:

iex> "foo" <> x = "foobar"
iex> x
"bar"

x <> "bar" = "foobar" привело бы к исключению CompileError.

left == right

term() == term() :: boolean()

Возвращает true если два элемента равны.

Этот оператор считает 1 и 1.0 равными. Для более строгой семантики используйте ===/2.

Все термины в Elixir могут сравниваться друг с другом.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 == 2
false

iex> 1 == 1.0
true

left === right

term() === term() :: boolean()

Возвращает true если два элемента точно равны.

Элементы считаются точно равными только в том случае, если они имеют одинаковое значение и тип. Например, 1 == 1.0 возвращает true, но поскольку они имеют разные типы, 1 === 1.0 возвращает false.

Все термины в Elixir могут сравниваться друг с другом.

Разрешено в условиях. Встроен компилятором.

Примеры

iex> 1 === 2
false

iex> 1 === 1.0
false

left =~ right

String.t() =~ (String.t() | Regex.t()) :: boolean()

Сопоставляет термин слева с регулярным выражением или строкой справа.

Возвращает true если left соответствует right (если это регулярное выражение) или содержит right (если это строка).

Примеры

iex> "abcd" =~ ~r/c(d)/
true

iex> "abcd" =~ ~r/e/
false

iex> "abcd" =~ "bc"
true

iex> "abcd" =~ "ad"
false

iex> "abcd" =~ ""
true

left > right

term() > term() :: boolean()

Возвращает true если left больше, чем right.

Все термины в Elixir могут быть сравнены друг с другом.

Разрешено в тестах условий. Встраивается компилятором.

Примеры

iex> 1 > 2
false

left >= right

term() >= term() :: boolean()

Возвращает true если left больше или равно right.

Все термины в Elixir могут быть сравнены друг с другом.

Разрешено в тестах условий. Встраивается компилятором.

Примеры

iex> 1 >= 2
false

@expr (макрос)

Читает и записывает атрибуты текущего модуля.

Канонический пример атрибутов — аннотирование, что модуль реализует поведение OTP, например GenServer:

defmodule MyServer do
  @behaviour GenServer
  # ... callbacks ...
end

По умолчанию Elixir поддерживает все атрибуты модулей, поддерживаемые Erlang, но также можно использовать пользовательские атрибуты:

defmodule MyServer do
  @my_data 13
  IO.inspect @my_data #=> 13
end

В отличие от Erlang, такие атрибуты по умолчанию не сохраняются в модуле, так как в Elixir принято использовать пользовательские атрибуты для хранения временных данных, которые будут доступны во время компиляции. Пользовательские атрибуты можно настроить, чтобы они вели себя как в Erlang, используя Module.register_attribute/3.

И наконец, обратите внимание, что атрибуты также можно читать внутри функций:

defmodule MyServer do
  @my_data 11
  def first_data, do: @my_data
  @my_data 13
  def second_data, do: @my_data
end

MyServer.first_data #=> 11
MyServer.second_data #=> 13

Важно отметить, что чтение атрибута создаёт моментальный снимок его текущего значения. Другими словами, значение читается во время компиляции, а не во время выполнения. Обратитесь к модулю Module для получения других функций управления атрибутами модуля.

abs(number)

abs(number()) :: number()

Возвращает целое число или число с плавающей точкой, которое является абсолютным значением number.

Разрешено в тестах условий. Встраивается компилятором.

Примеры

iex> abs(-3.33)
3.33

iex> abs(-3)
3

alias!(alias) (макрос)

Когда используется внутри цитирования, отмечает, что данный псевдоним не должен быть гигиенизирован. Это означает, что псевдоним будет расширен при расширении макроса.

См. Kernel.SpecialForms.quote/2 для получения дополнительной информации.

left and right (макрос)

Булево И.

Если left является false, возвращает false; в противном случае возвращает right.

Требуется, чтобы только операнд left был булевым, так как он выполняется по короткому замыканию. Если операнд left не является булевым, генерируется исключение ArgumentError.

Разрешено в тестах условий.

Примеры

iex> true and false
false
iex> true and "yay!"
"yay!"

apply(fun, args)

apply((... -> any()), [any()]) :: any()

Вызывает переданную анонимную функцию fun со списком аргументов args.

Встраивается компилятором.

Примеры

iex> apply(fn x -> x * 2 end, [2])
4

apply(module, function_name, args)

apply(module(), function_name :: atom(), [any()]) :: any()

Вызывает переданную функцию из module со списком аргументов args.

apply/3 используется для вызова функций, где модуль, имя функции или аргументы определяются динамически во время выполнения. По этой причине вы не можете вызывать макросы с помощью apply/3, только функции.

Встраивается компилятором.

Примеры

iex> apply(Enum, :reverse, [[1, 2, 3]])
[3, 2, 1]

binary_part(binary, start, length)

binary_part(binary(), non_neg_integer(), integer()) :: binary()

Извлекает часть двоичного объекта, начиная с start длиной length. Двоичные объекты индексируются с нуля.

Если start или length каким-либо образом ссылаются за пределы двоичного объекта, генерируется исключение ArgumentError.

Разрешено в тестах условий. Встраивается компилятором.

Примеры

iex> binary_part("foo", 1, 2)
"oo"

Отрицательное значение length может использоваться для извлечения байтов, которые находятся перед байтом в позиции start:

iex> binary_part("Hello", 5, -3)
"llo"

binding(context \\ nil) (макрос)

Возвращает привязку для заданного контекста в виде списка ключевых слов.

В возвращаемом результате ключи — имена переменных, а значения — соответствующие значения переменных.

Если заданный context равен nil (по умолчанию это так), возвращается привязка для текущего контекста.

Примеры

iex> x = 1
iex> binding()
[x: 1]
iex> x = 2
iex> binding()
[x: 2]

iex> binding(:foo)
[]
iex> var!(x, :foo) = 1
1
iex> binding(:foo)
[x: 1]

bit_size(bitstring)

bit_size(bitstring()) :: non_neg_integer()

Возвращает целое число, которое является размером в битах bitstring.

Разрешено в тестах условий. Встраивается компилятором.

Примеры

iex> bit_size(<<433::16, 3::3>>)
19

iex> bit_size(<<1, 2, 3>>)
24

byte_size(bitstring)

byte_size(bitstring()) :: non_neg_integer()

Возвращает количество байтов, необходимых для хранения bitstring.

То есть, если количество битов в bitstring не делится на 8, полученное количество байтов будет округлено вверх (по избытку). Эта операция выполняется за постоянное время.

Разрешено в тестах условий. Встраивается компилятором.

Примеры

iex> byte_size(<<433::16, 3::3>>)
3

iex> byte_size(<<1, 2, 3>>)
3

def(call, expr \\ nil) (макрос)

Определяет функцию с заданным именем и телом.

Примеры

defmodule Foo do
  def bar, do: :baz
end

Foo.bar() #=> :baz

Функция, ожидающая аргументы, может быть определена следующим образом:

defmodule Foo do
  def sum(a, b) do
    a + b
  end
end

В примере выше определяется функция sum/2; эта функция принимает два аргумента и возвращает их сумму.

Аргументы по умолчанию

\\ используется для указания значения по умолчанию для параметра функции. Например:

defmodule MyMath do
  def multiply_by(number, factor \\ 2) do
    number * factor
  end
end

MyMath.multiply_by(4, 3) #=> 12
MyMath.multiply_by(4) #=> 8

Компилятор преобразует это в несколько функций с различными арностями, здесь Foo.multiply_by/1 и Foo.multiply_by/2, которые представляют случаи, когда аргументы для параметров с значениями по умолчанию передаются или нет.

При определении функции с аргументами по умолчанию и несколькими явно объявленными разделами, вы должны написать заголовок функции, в котором объявлены значения по умолчанию. Например:

defmodule MyString do
  def join(string1, string2 \\ nil, separator \\ " ")

  def join(string1, nil, _separator) do
    string1
  end

  def join(string1, string2, separator) do
    string1 <> separator <> string2
  end
end

Обратите внимание, что \\ нельзя использовать с анонимными функциями, потому что они могут иметь только одну арность.

Имена функций и переменных

Имена функций и переменных имеют следующий синтаксис: строчная буква ASCII или символ подчеркивания, за которыми следует любое количество строчных или прописных букв ASCII, цифр или символов подчеркивания. Допускается окончание символами «! » или «?».

Для переменных любой идентификатор, начинающийся с подчеркивания, должен указывать на неиспользуемую переменную. Например:

def foo(bar) do
  []
end
#=> warning: variable bar is unused

def foo(_bar) do
  []
end
#=> no warning

def foo(_bar) do
  _bar
end
#=> warning: the underscored variable "_bar" is used after being set

rescue/catch/after

Тела функций поддерживают rescue, catch и after как делает Kernel.SpecialForms.try/1. Следующие две функции эквивалентны:

def format(value) do
  try do
    format!(value)
  catch
    :exit, reason -> {:error, reason}
  end
end

def format(value) do
  format!(value)
catch
  :exit, reason -> {:error, reason}
end

defdelegate(funs, opts) (макрос)

Определяет функцию, которая делегирует вызов другому модулю.

Функции, определённые с помощью defdelegate/2, являются общедоступными и могут вызываться извне модуля, в котором они определены (как если бы они были определены с помощью def/2). Если нужно делегировать вызов как частным функциям, необходимо использовать import/2.

Делегирование работает только с функциями; делегирование макросов не поддерживается.

См. def/2 для правил именования и аргументов по умолчанию.

Параметры

  • :to - модуль, к которому производится перенаправление.

  • :as - функция для вызова в целевом модуле, указанном в :to. Этот параметр является необязательным и по умолчанию имеет то же имя, что и делегируемая функция (funs).

Примеры

defmodule MyList do
  defdelegate reverse(list), to: :lists
  defdelegate other_reverse(list), to: :lists, as: :reverse
end

MyList.reverse([1, 2, 3])
#=> [3, 2, 1]

MyList.other_reverse([1, 2, 3])
#=> [3, 2, 1]

defexception(fields) (макрос)

Определяет исключение.

Исключения — это структуры, поддерживаемые модулем, реализующим поведение Exception. Поведение Exception требует реализации двух функций:

  • exception/1 - получает аргументы, переданные в raise/2, и возвращает структуру исключения. По умолчанию эта функция принимает набор аргументов в виде ключевых слов, которые объединяются в структуру, или строку, которая используется в качестве сообщения исключения.

  • message/1 - получает структуру исключения и должна вернуть её сообщение. Чаще всего исключения имеют поле сообщения, к которому по умолчанию обращается эта функция. Однако, если у исключения нет поля сообщения, эта функция должна быть реализована явно.

Поскольку исключения являются структурами, API, поддерживаемый defstruct/1, также доступен в defexception/1.

Выброс исключений

Наиболее распространённый способ поднятия исключения — с помощью raise/2:

defmodule MyAppError do
  defexception [:message]
end

value = [:hello]

raise MyAppError,
  message: "did not get what was expected, got: #{inspect value}"

Во многих случаях удобнее передать ожидаемое значение в raise/2 и сгенерировать сообщение в обратном вызове Exception.exception/1:

defmodule MyAppError do
  defexception [:message]

  @impl true
  def exception(value) do
    msg = "did not get what was expected, got: #{inspect value}"
    %MyAppError{message: msg}
  end
end

raise MyAppError, value

Приведённый выше пример демонстрирует предпочтительную стратегию настройки сообщений об ошибках.

defguard(guard) (макрос) (с версии 1.6.0)

defguard(Macro.t()) :: Macro.t()

Генерирует макрос, подходящий для использования в выражениях стражей.

Вызывает ошибку во время компиляции, если определение использует выражения, запрещённые в стражах, в противном случае создаёт макрос, который можно использовать как внутри, так и вне стражей.

Обратите внимание на конвенцию в Elixir: имена функций/макросов, разрешённых в стражах, начинаются с префикса is_, например is_list/1. Однако, если функция/макрос возвращает булево значение и не разрешена в стражах, она не должна иметь префикс и должна заканчиваться знаком вопроса, например Keyword.keyword?/1.

Пример

defmodule Integer.Guards do
  defguard is_even(value) when is_integer(value) and rem(value, 2) == 0
end

defmodule Collatz do
  @moduledoc "Tools for working with the Collatz sequence."
  import Integer.Guards

  @doc "Determines the number of steps `n` takes to reach `1`."
  # If this function never converges, please let me know what `n` you used.
  def converge(n) when n > 0, do: step(n, 0)

  defp step(1, step_count) do
    step_count
  end

  defp step(n, step_count) when is_even(n) do
    step(div(n, 2), step_count + 1)
  end

  defp step(n, step_count) do
    step(3*n + 1, step_count + 1)
  end
end

defguardp(guard) (макрос) (с версии 1.6.0)

defguardp(Macro.t()) :: Macro.t()

Генерирует частный макрос, подходящий для использования в выражениях стражей.

Вызывает ошибку во время компиляции, если определение использует выражения, запрещённые в стражах, в противном случае создаёт частный макрос, который можно использовать как внутри, так и вне стражей в текущем модуле.

Аналогично defmacrop/2, defguardp/1 должен быть определён до использования в текущем модуле.

defimpl(name, opts, do_block \\ []) (макрос)

Определяет реализацию для данного протокола.

См. defprotocol/2 для получения дополнительной информации и примеров протоколов.

Внутри реализации имя протокола можно получить с помощью @protocol, а текущую цель — с помощью @for.

defmacro(call, expr \\ nil) (макрос)

Определяет макрос с данным именем и телом.

См. def/2 для правил именования и аргументов по умолчанию.

Примеры

defmodule MyLogic do
  defmacro unless(expr, opts) do
    quote do
      if !unquote(expr), unquote(opts)
    end
  end
end

require MyLogic
MyLogic.unless false do
  IO.puts "It works"
end

defmacrop(call, expr \\ nil) (макрос)

Определяет частный макрос с данным именем и телом.

Частные макросы доступны только из того же модуля, в котором они определены.

См. defmacro/2 для получения дополнительной информации и def/2 для правил именования и аргументов по умолчанию.

defmodule(alias, do_block) (макрос)

Определяет модуль с заданным именем и содержимым.

Этот макрос определяет модуль с заданным alias в качестве имени и заданным содержимым. Возвращает кортеж из четырёх элементов:

  • :module
  • имя модуля
  • двоичное содержимое модуля
  • результат вычисления блока содержимого

Примеры

iex> defmodule Foo do
...>   def bar, do: :baz
...> end
iex> Foo.bar()
:baz

Вложение

Вложение модуля внутри другого модуля влияет на имя вложенного модуля:

defmodule Foo do
  defmodule Bar do
  end
end

В примере выше создаются два модуля — Foo и Foo.Bar. При вложении Elixir автоматически создаёт псевдоним для внутреннего модуля, позволяя обращаться ко второму модулю Foo.Bar как к Bar в той же лексической области, где он определён (модуль Foo).

Если модуль Foo.Bar переместить в другое место, ссылки на Bar в модуле Foo необходимо обновить на полное имя (Foo.Bar) или явно задать псевдоним в модуле Foo с помощью Kernel.SpecialForms.alias/2.

defmodule Foo.Bar do
  # code
end

defmodule Foo do
  alias Foo.Bar
  # code here can refer to "Foo.Bar" as just "Bar"
end

Имена модулей

Имя модуля может быть любым атомом, но Elixir предоставляет специальный синтаксис, обычно используемый для имён модулей. Имя модуля — это заглавная латинская буква, за которой следует любое количество строчных или заглавных латинских букв, цифр или подчеркиваний. Этот идентификатор эквивалентен атому, дополненному префиксом Elixir.. Так, в примере defmodule Foo Foo эквивалентен :"Elixir.Foo"

Динамические имена

Имена модулей в Elixir могут быть динамически сгенерированы. Это очень полезно при работе с макросами. Например, можно написать:

defmodule String.to_atom("Foo#{1}") do
  # contents ...
end

Elixir примет любое имя модуля, если выражение, переданное в качестве первого аргумента в defmodule/2, вычисляется в атом. Обратите внимание, что при использовании динамического имени Elixir не будет вкладывать имя в текущий модуль и не будет автоматически устанавливать псевдоним.

defoverridable(keywords_or_behaviour) (макрос)

Делает заданные функции в текущем модуле переопределяемыми.

Переопределяемая функция определяется лениво, позволяя разработчику её переопределить.

Пример

defmodule DefaultMod do
  defmacro __using__(_opts) do
    quote do
      def test(x, y) do
        x + y
      end

      defoverridable [test: 2]
    end
  end
end

defmodule InheritMod do
  use DefaultMod

  def test(x, y) do
    x * y + super(x, y)
  end
end

Как видно из примера выше, super можно использовать для вызова реализации по умолчанию.

Если @behaviour определена, defoverridable также можно вызвать с модулем в качестве аргумента. Все реализованные обратные вызовы из поведения выше вызова defoverridable будут помечены как переопределяемые.

Пример

defmodule Behaviour do
  @callback foo :: any
end

defmodule DefaultMod do
  defmacro __using__(_opts) do
    quote do
      @behaviour Behaviour

      def foo do
        "Override me"
      end

      defoverridable Behaviour
    end
  end
end

defmodule InheritMod do
  use DefaultMod

  def foo do
    "Overridden"
  end
end

defp(call, expr \\ nil) (макрос)

Определяет частную функцию с заданным именем и телом.

Частные функции доступны только из модуля, в котором они определены. Попытка получить доступ к частной функции извне модуля, в котором она определена, приводит к исключению UndefinedFunctionError.

См. def/2 для получения дополнительной информации.

Примеры

defmodule Foo do
  def bar do
    sum(1, 2)
  end

  defp sum(a, b), do: a + b
end

Foo.bar() #=> 3
Foo.sum(1, 2) #=> ** (UndefinedFunctionError) undefined function Foo.sum/2

defprotocol(name, do_block) (макрос)

Определяет протокол.

Протокол определяет API, который должны реализовать его реализации.

Примеры

В Elixir есть два глагола для проверки количества элементов в структуре данных: length и size. length означает, что информация должна быть вычислена. Например, length(list) нужно пройти по всему списку для расчёта его длины. С другой стороны, tuple_size(tuple) и byte_size(binary) не зависят от размера кортежа и бинарного файла, так как информация о размере предварительно вычислена в структуре данных.

Хотя Elixir включает в себя такие функции, как tuple_size, binary_size и map_size, иногда мы хотим получить размер структуры данных независимо от её типа. В Elixir можно писать полиморфный код, т. е. код, работающий с различными типами, используя протоколы. Протокол размера можно реализовать следующим образом:

defprotocol Size do
  @doc "Calculates the size (and not the length!) of a data structure"
  def size(data)
end

Теперь протокол можно реализовать для любой структуры данных, которую может содержать протокол:

defimpl Size, for: BitString do
  def size(binary), do: byte_size(binary)
end

defimpl Size, for: Map do
  def size(map), do: map_size(map)
end

defimpl Size, for: Tuple do
  def size(tuple), do: tuple_size(tuple)
end

Обратите внимание, что мы не реализовали его для списков, так как у списков нет size информации, а её значение нужно вычислить с помощью length.

Можно реализовать протоколы для всех типов Elixir:

  • Структуры (см. ниже)
  • Tuple
  • Atom
  • List
  • BitString
  • Integer
  • Float
  • Function
  • PID
  • Map
  • Port
  • Reference
  • Any (см. ниже)

Протоколы и структуры

Настоящая польза протоколов проявляется при их использовании совместно со структурами. Например, Elixir поставляется со многими типами данных, реализованными как структуры, такими как MapSet. Мы также можем реализовать протокол Size для этих типов:

defimpl Size, for: MapSet do
  def size(map_set), do: MapSet.size(map_set)
end

При реализации протокола для структуры опцию :for можно опустить, если вызов defimpl находится в модуле, определяющем структуру:

defmodule User do
  defstruct [:email, :name]

  defimpl Size do
    def size(%User{}), do: 2 # two fields
  end
end

Если реализация протокола для данного типа не найдена, вызов протокола вызовет исключение, если не настроена отмена по умолчанию к Any. Доступны удобства для построения реализаций на основе существующих, см. defstruct/1 для получения дополнительной информации о производных протоколах.

Отмена по умолчанию к любому

В некоторых случаях может быть удобно предоставить реализацию по умолчанию для всех типов. Это можно сделать, установив атрибут @fallback_to_any в true в определении протокола:

defprotocol Size do
  @fallback_to_any true
  def size(data)
end

Протокол Size теперь можно реализовать для Any:

defimpl Size, for: Any do
  def size(_), do: 0
end

Хотя реализация выше, вероятно, не является разумной. Например, нет смысла говорить, что PID или целое число имеют размер 0. Это одна из причин, почему @fallback_to_any — это поведенческий принцип "opt-in". Для большинства протоколов поднимание ошибки при отсутствии реализации протокола — это правильное поведение.

Несколько реализаций

Протоколы также могут быть реализованы для нескольких типов одновременно:

defprotocol Reversible do
  def reverse(term)
end

defimpl Reversible, for: [Map, List] do
  def reverse(term), do: Enum.reverse(term)
end

Типы

Определение протокола автоматически определяет тип с именем t, который можно использовать следующим образом:

@spec print_size(Size.t) :: :ok
def print_size(data) do
  IO.puts(case Size.size(data) do
    0 -> "data has no items"
    1 -> "data has one item"
    n -> "data has #{n} items"
  end)
end

Вышеупомянутое @spec указывает, что все типы, разрешенные для реализации данного протокола, являются допустимыми типами аргументов для данной функции.

Рефлексия

Любой модуль протокола содержит три дополнительные функции:

  • __protocol__/1 — возвращает имя протокола, когда :name передается, список ключевых слов с функциями протокола и их арностью, когда :functions передается, и список реализаций, когда :impls передается

  • impl_for/1 — получает структуру и возвращает модуль, реализующий протокол для структуры, nil в противном случае

  • impl_for!/1 — то же самое, что и выше, но генерирует ошибку, если реализация не найдена

    Enumerable.__protocol__(:functions)
    #=> [count: 1, member?: 2, reduce: 3]
    
    Enumerable.impl_for([])
    #=> Enumerable.List
    
    Enumerable.impl_for(42)
    #=> nil

Консолидация

Для обработки загрузки кода в режиме разработки протоколы в Elixir предоставляют медленную реализацию диспетчеризации протоколов, специфичную для разработки.

Для ускорения диспетчеризации в производственных средах, где все реализации известны заранее, Elixir предоставляет функцию консолидации протоколов. Консолидация напрямую связывает протоколы с их реализациями таким образом, что вызов функции из консолидированного протокола эквивалентен вызову двух удаленных функций.

Консолидация протоколов применяется по умолчанию ко всем проектам Mix во время компиляции. Это может быть проблемой во время тестирования. Например, если вы хотите реализовать протокол во время тестирования, реализация не повлияет, поскольку протокол уже был консолидирован. Одним из возможных решений является включение каталогов компиляции, специфичных для вашей тестовой среды, в ваш mix.exs:

def project do
  ...
  elixirc_paths: elixirc_paths(Mix.env)
  ...
end

defp elixirc_paths(:test), do: ["lib", "test/support"]
defp elixirc_paths(_), do: ["lib"]

Затем вы можете определить реализации, специфичные для тестовой среды, внутри test/support/some_file.ex.

Другой подход заключается в отключении консолидации протоколов во время тестирования в вашем mix.exs:

def project do
  ...
  consolidate_protocols: Mix.env != :test
  ...
end

Хотя это не рекомендуется, так как это может повлиять на производительность вашего набора тестов.

Наконец, обратите внимание, что все протоколы компилируются с debug_info установленным в true, независимо от варианта, установленного компилятором elixirc. Информация отладки используется для консолидации и может быть удалена после консолидации.

defstruct(fields) (макрос)

Определяет структуру.

Структура — это помеченная карта, которая позволяет разработчикам предоставлять значения по умолчанию для ключей, метки, используемые в полиморфных диспетчеризациях и утверждениях во время компиляции.

Чтобы определить структуру, разработчик должен определить как __struct__/0 , так и __struct__/1 функции. defstruct/1 — это макрос удобства, который определяет такие функции с некоторыми удобствами.

Для получения дополнительной информации о структурах, пожалуйста, ознакомьтесь со статьей Kernel.SpecialForms.%/2.

Примеры

defmodule User do
  defstruct name: nil, age: nil
end

Поля структуры вычисляются во время компиляции, что позволяет им быть динамическими. В примере ниже 10 + 11 вычисляется во время компиляции, а поле age хранится со значением 21:

defmodule User do
  defstruct name: nil, age: 10 + 11
end

Аргумент fields обычно представляет собой список ключевых слов, в котором имена полей выступают в качестве атомарных ключей, а значения по умолчанию — соответствующими значениями. defstruct/1 также поддерживает список атомов в качестве аргумента: в этом случае атомы в списке будут использоваться в качестве имён полей структуры, и все они будут иметь значение по умолчанию nil.

defmodule Post do
  defstruct [:title, :content, :author]
end

Наследование

Хотя структуры являются картами, по умолчанию структуры не реализуют ни одного из протоколов, реализованных для карт. Например, попытка использовать протокол со структурой User приводит к ошибке:

john = %User{name: "John"}
MyProtocol.call(john)
** (Protocol.UndefinedError) protocol MyProtocol not implemented for %User{...}

defstruct/1, однако, позволяет реализовывать протоколы наследованием. Это можно сделать, определив атрибут @derive как список перед вызовом defstruct/1:

defmodule User do
  @derive [MyProtocol]
  defstruct name: nil, age: 10 + 11
end

MyProtocol.call(john) #=> works

Для каждого протокола в списке @derive Elixir будет утверждать, что существует реализация этого протокола для любого (независимо от того, является ли обращение к любому true) и проверять, определяет ли любая реализация обратный вызов __deriving__/3 (через Protocol.derive/3). Если да, то обратный вызов вызывается, в противном случае автоматически выводится реализация, которая просто указывает на реализацию any.

Принудительное использование ключей

При создании структуры Elixir автоматически гарантирует, что все ключи принадлежат структуре:

%User{name: "john", unknown: :key}
** (KeyError) key :unknown not found in: %User{age: 21, name: nil}

Elixir также позволяет разработчикам принудительно использовать определенные ключи, которые всегда должны указываться при создании структуры:

defmodule User do
  @enforce_keys [:name]
  defstruct name: nil, age: 10 + 11
end

Теперь попытка создать структуру без ключа name завершится ошибкой:

%User{age: 21}
** (ArgumentError) the following keys must also be given when building struct User: [:name]

Обратите внимание, что @enforce_keys представляет собой просто гарантию времени компиляции, помогающую разработчикам при создании структур. Она не применяется к обновлениям и не предоставляет никакой проверки значений.

Типы

Рекомендуется определять типы для структур. По соглашению такой тип называется t. Чтобы определить структуру внутри типа, используется синтаксис литерала структуры:

defmodule User do
  defstruct name: "John", age: 25
  @type t :: %User{name: String.t, age: non_neg_integer}
end

Рекомендуется использовать синтаксис структуры только при определении типа структуры. При ссылке на другую структуру лучше использовать User.t вместо %User{}.

Типы полей структуры, которые не включены в %User{} , по умолчанию являются term.

Структуры, внутренняя структура которых является приватной для локального модуля (сопоставление с образцом или прямой доступ к их полям не должны быть разрешены), должны использовать атрибут @opaque. Структуры, внутренняя структура которых является публичной, должны использовать @type.

destructure(left, right) (макрос)

Деструктурирует два списка, присваивая каждый элемент в правом списке соответствующему элементу в левом.

В отличие от сопоставления с образцом через =, если размеры левого и правого списков не совпадают, деструктурирование просто останавливается, а не генерирует ошибку.

Примеры

iex> destructure([x, y, z], [1, 2, 3, 4, 5])
iex> {x, y, z}
{1, 2, 3}

В примере выше, даже если в правом списке больше элементов, чем в левом, деструктурирование работает без проблем. Если правый список меньше, оставшиеся элементы просто устанавливаются в nil:

iex> destructure([x, y, z], [1])
iex> {x, y, z}
{1, nil, nil}

Левая сторона поддерживает любые выражения, которые вы бы использовали в левой части соответствия:

x = 1
destructure([^x, y, z], [1, 2, 3])

Приведенный выше пример будет работать только в том случае, если x совпадает с первым значением в правом списке. В противном случае он сгенерирует MatchError (как и оператор =).

div(dividend, divisor)

div(integer(), neg_integer() | pos_integer()) :: integer()

Выполняет целочисленное деление.

Генерирует исключение ArithmeticError, если один из аргументов не является целым числом или когда divisor является 0.

div/2 выполняет усеченное целочисленное деление. Это означает, что результат всегда округляется к нулю.

Если вам нужно выполнить целочисленное деление с округлением к нулю (округление к отрицательной бесконечности), используйте Integer.floor_div/2 вместо этого.

Разрешено в тестах охранных условий. Встраивается компилятором.

Примеры

div(5, 2)
#=> 2

div(6, -4)
#=> -1

div(-99, 2)
#=> -49

div(100, 0)
#=> ** (ArithmeticError) bad argument in arithmetic expression

elem(tuple, index)

elem(tuple(), non_neg_integer()) :: term()

Получает элемент по нулевому индексу index в tuple.

Генерирует ArgumentError, когда индекс отрицательный или выходит за пределы диапазона элементов кортежа.

Разрешено в тестах охранных условий. Встраивается компилятором.

Примеры

tuple = {:foo, :bar, 3}
elem(tuple, 1)
#=> :bar

elem({}, 0)
#=> ** (ArgumentError) argument error

elem({:foo, :bar}, 2)
#=> ** (ArgumentError) argument error

exit(reason)

exit(term()) :: no_return()

Прекращает выполнение вызывающего процесса с указанным основанием.

Поскольку вычисление этой функции приводит к завершению процесса, она не имеет возвращаемого значения.

Встраивается компилятором.

Примеры

При завершении процесса по умолчанию он завершается с основанием :normal. Вы также можете явно вызвать exit/1, если хотите завершить процесс, но не сигнализировать об ошибке:

exit(:normal)

В случае возникновения проблемы вы также можете использовать exit/1 с другим основанием:

exit(:seems_bad)

Если причина выхода не :normal, все процессы, связанные с вышедшим процессом, потерпят крах (если они не обрабатывают выходы).

OTP-выходы

Выходы используются OTP для определения, завершился ли процесс аномально или нет. Следующие выходы считаются «нормальными»:

  • exit(:normal)
  • exit(:shutdown)
  • exit({:shutdown, term})

Выход с любой другой причиной считается аномальным и обрабатывается как крах. Это означает, что срабатывает поведение по умолчанию для надзирателя, генерируются отчеты об ошибках и т. д.

Это поведение используется во многих местах. Например, ExUnit использует exit(:shutdown) при выходе из тестового процесса, чтобы сигнализировать связанным процессам, деревьям управления и т. д. о вежливом завершении работы тоже.

Выходы CLI

Основываясь на сигналах выхода, упомянутых выше, если процесс, запущенный из командной строки, завершается с любой из трех вышеперечисленных причин, его выход считается нормальным, и процесс операционной системы завершится со статусом 0.

Однако можно настроить сигнал выхода операционной системы, вызвав:

exit({:shutdown, integer})

Это заставит процесс ОС завершиться со статусом, указанным integer, одновременно сигнализируя всем связанным процессам Erlang о вежливом завершении работы.

Любая другая причина выхода приведет к завершению процесса ОС со статусом 1 и к краху связанных процессов Erlang.

function_exported?(module, function, arity)

function_exported?(module(), atom(), arity()) :: boolean()

Возвращает true если module загружен и содержит публичный function с заданным arity, в противном случае false.

Обратите внимание, что эта функция не загружает модуль в случае, если он не загружен. См. Code.ensure_loaded/1 для получения дополнительной информации.

Встроено компилятором.

Примеры

iex> function_exported?(Enum, :member?, 2)
true

get_and_update_in(path, fun) (макрос)

Получает значение и обновляет вложенную структуру данных с помощью заданной path.

Это аналогично get_and_update_in/3, за исключением того, что путь извлекается с помощью макроса, а не передаётся в виде списка. Например:

get_and_update_in(opts[:foo][:bar], &{&1, &1 + 1})

Эквивалентно:

get_and_update_in(opts, [:foo, :bar], &{&1, &1 + 1})

Обратите внимание, что для работы этого макроса весь путь должен быть всегда виден этим макросом. См. раздел Пути ниже.

Примеры

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users["john"].age, &{&1, &1 + 1})
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}

Пути

Путь может начинаться с переменной, локального или удалённого вызова и должен быть последовать одним или несколькими:

  • foo[bar] - обращается к ключу bar в foo; в случае, если foo равно nil, возвращается nil

  • foo.bar - обращается к полю карты/структуры; в случае отсутствия поля, возникает ошибка

Вот некоторые допустимые пути:

users["john"][:age]
users["john"].age
User.all["john"].age
all_users()["john"].age

Вот некоторые недопустимые:

# Does a remote call after the initial value
users["john"].do_something(arg1, arg2)

# Does not access any key or field
users

get_and_update_in(data, keys, fun)

get_and_update_in(
  structure :: Access.t(),
  keys,
  (term() -> {get_value, update_value} | :pop)
) :: {get_value, structure :: Access.t()}
when keys: [any(), ...], update_value: term(), get_value: var

Получает значение и обновляет вложенную структуру.

data — это вложенная структура (то есть карта, список ключевых слов или структура, которая реализует поведение Access).

Аргумент fun получает значение key (или nil если key отсутствует) и должен вернуть одно из следующих значений:

  • двухэлементная кортеж {get_value, new_value}. В этом случае get_value — это полученное значение, которое можно обработать перед возвратом. new_value — новое значение, которое будет храниться под key.

  • :pop, что подразумевает, что текущее значение под key должно быть удалено из структуры и возвращено.

Эта функция использует модуль Access для обхода структур в соответствии с заданным keys, если key не является функцией.

Если ключ — функция, функция будет вызвана с тремя аргументами:

  • операция (:get_and_update)
  • данные для доступа
  • функция, которая будет вызвана дальше

Это означает, что get_and_update_in/3 может быть расширена для предоставления пользовательских запросов. Недостатком является то, что функции не могут храниться в качестве ключей в доступных структурах данных.

Примеры

Эта функция полезна, когда необходимо получить текущее значение (или что-то вычисленное в зависимости от текущего значения) и обновить его одновременно. Например, она может использоваться для чтения текущего возраста пользователя, одновременно увеличивая его на единицу за один проход:

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["john", :age], &{&1, &1 + 1})
{27, %{"john" => %{age: 28}, "meg" => %{age: 23}}}

Когда один из ключей — функция, функция вызывается. В примере ниже мы используем функцию для получения и инкрементирования всех возрастов в списке:

iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> all = fn :get_and_update, data, next ->
...>   data |> Enum.map(next) |> Enum.unzip()
...> end
iex> get_and_update_in(users, [all, :age], &{&1, &1 + 1})
{[27, 23], [%{name: "john", age: 28}, %{name: "meg", age: 24}]}

Если предыдущее значение перед вызовом функции было nil, функция будет получать nil в качестве значения и должна обработать его соответствующим образом (будь то сбой или предоставление разумного значения по умолчанию).

Модуль Access поставляется с множеством удобных функций-акссессоров, например, анонимной функцией all определённой выше. См. Access.all/0, Access.key/2 и другие в качестве примеров.

get_in(data, keys)

get_in(Access.t(), [term(), ...]) :: term()

Получает значение из вложенной структуры.

Использует модуль Access для обхода структур в соответствии с заданными keys, если key не является функцией.

Если ключ — функция, функция будет вызвана с тремя аргументами:

  • операция (:get)
  • данные для доступа
  • функция, которая будет вызвана дальше

Это означает, что get_in/2 может быть расширена для предоставления пользовательских запросов. Недостатком является то, что функции не могут храниться в качестве ключей в доступных структурах данных.

Примеры

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_in(users, ["john", :age])
27

В случае, если какие-либо из элементов посередине возвращают nil, nil будет возвращено в соответствии с модулем Access:

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_in(users, ["unknown", :age])
nil

Когда один из ключей — функция, принимающая три аргумента, функция вызывается. В примере ниже мы используем функцию для получения всех карт внутри списка:

iex> users = [%{name: "john", age: 27}, %{name: "meg", age: 23}]
iex> all = fn :get, data, next -> Enum.map(data, next) end
iex> get_in(users, [all, :age])
[27, 23]

Если предыдущее значение перед вызовом функции было nil, функция будет получать nil в качестве значения и должна обработать его соответствующим образом.

hd(list)

hd(nonempty_maybe_improper_list(elem, any())) :: elem when elem: term()

Возвращает голову списка. Вызывает ArgumentError, если список пуст.

Работает со списками неправильной структуры.

Разрешено в тестах условий. Встроено компилятором.

Примеры

hd([1, 2, 3, 4])
#=> 1

hd([])
#=> ** (ArgumentError) argument error

hd([1 | 2])
#=> 1

if(condition, clauses) (макрос)

Предоставляет макрос if/2.

Этот макрос ожидает, что первый аргумент будет условием, а второй — списком ключевых слов.

Примеры однострочных выражений

if(foo, do: bar)

В примере выше, bar будет возвращено, если foo вычисляется как true (то есть ни false ни nil). В противном случае, будет возвращено nil.

Можно указать опцию else, чтобы указать обратное:

if(foo, do: bar, else: baz)

Примеры блоков

Также можно передать блок в макрос if/2. Первый пример выше будет преобразован в:

if foo do
  bar
end

Обратите внимание, что do/end становятся разделителями. Второй пример будет преобразован в:

if foo do
  bar
else
  baz
end

Для сравнения более чем двух условий необходимо использовать макрос cond/1.

left in right (макрос)

Проверяет, является ли элемент слева членом коллекции справа.

Примеры

iex> x = 1
iex> x in [1, 2, 3]
true

Этот оператор (который является макросом) просто преобразуется в вызов Enum.member?/2. Пример выше будет преобразован в:

Enum.member?([1, 2, 3], x)

Elixir также поддерживает left not in right, которое вычисляется как not(left in right):

iex> x = 1
iex> x not in [1, 2, 3]
false

Условия

Оператор in/2 (а также not in) может использоваться в условиях, если правая часть — диапазон или список. В таких случаях Elixir преобразует оператор в допустимое выражение условия. Например:

when x in [1, 2, 3]

преобразуется в:

when x === 1 or x === 2 or x === 3

При использовании диапазонов:

when x in 1..3

преобразуется в:

when is_integer(x) and x >= 1 and x <= 3

Обратите внимание, что только целые числа могут рассматриваться внутри диапазона с помощью in.

Соображения по AST

left not in right парсится компилятором в AST:

{:not, _, [{:in, _, [left, right]}]}

Это тот же AST, что и not(left in right).

Кроме того, Macro.to_string/2 преобразует все вхождения этого AST в left not in right.

inspect(term, opts \\ [])

inspect(Inspect.t(), keyword()) :: String.t()

Просматривает заданный аргумент согласно протоколу Inspect. Второй аргумент — список ключевых слов с параметрами для управления просмотром.

Параметры

inspect/2 принимает список параметров, которые в итоге преобразуются в структуру Inspect.Opts. См. документацию по Inspect.Opts для получения списка поддерживаемых параметров.

Примеры

iex> inspect(:foo)
":foo"

iex> inspect([1, 2, 3, 4, 5], limit: 3)
"[1, 2, 3, ...]"

iex> inspect([1, 2, 3], pretty: true, width: 0)
"[1,\n 2,\n 3]"

iex> inspect("olá" <> <<0>>)
"<<111, 108, 195, 161, 0>>"

iex> inspect("olá" <> <<0>>, binaries: :as_strings)
"\"olá\\0\""

iex> inspect("olá", binaries: :as_binaries)
"<<111, 108, 195, 161>>"

iex> inspect('bar')
"'bar'"

iex> inspect([0 | 'bar'])
"[0, 98, 97, 114]"

iex> inspect(100, base: :octal)
"0o144"

iex> inspect(100, base: :hex)
"0x64"

Обратите внимание, что протокол Inspect не обязательно возвращает допустимое представление термина Elixir. В таких случаях результат проверки должен начинаться с #. Например, при проверке функции будет возвращено:

inspect fn a, b -> a + b end
#=> #Function<...>

is_atom(term)

is_atom(term()) :: boolean()

Возвращает true если term является атомом; в противном случае возвращает false.

Разрешено в тестах условий. Встроено компилятором.

is_binary(term)

is_binary(term()) :: boolean()

Возвращает true если term является бинарным; в противном случае возвращает false.

Бинарное всегда содержит целое число байт.

Разрешено в тестах условий. Встроено компилятором.

Примеры

iex> is_binary("foo")
true
iex> is_binary(<<1::3>>)
false

is_bitstring(term)

is_bitstring(term()) :: boolean()

Возвращает true если term является бистроингом (включая бинарный); в противном случае возвращает false.

Разрешено в тестах условий. Встроено компилятором.

Примеры

iex> is_bitstring("foo")
true
iex> is_bitstring(<<1::3>>)
true

is_boolean(term)

is_boolean(term()) :: boolean()

Возвращает true, если term является атомом true или атомом false (т.е. булевым значением); в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_float(term)

is_float(term()) :: boolean()

Возвращает true, если term является числом с плавающей точкой; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_function(term)

is_function(term()) :: boolean()

Возвращает true, если term является функцией; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_function(term, arity)

is_function(term(), non_neg_integer()) :: boolean()

Возвращает true если term является функцией, которая может быть применена с arity числом аргументов; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

Примеры

iex> is_function(fn x -> x * 2 end, 1)
true
iex> is_function(fn x -> x * 2 end, 2)
false

is_integer(term)

is_integer(term()) :: boolean()

Возвращает true, если term является целым числом; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_list(term)

is_list(term()) :: boolean()

Возвращает true, если term является списком с нулем или более элементами; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_map(term)

is_map(term()) :: boolean()

Возвращает true, если term является картой; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_nil(term) (макрос)

Возвращает true если term есть nil, false в противном случае.

Разрешено в охранных условиях.

Примеры

iex> is_nil(1)
false

iex> is_nil(nil)
true

is_number(term)

is_number(term()) :: boolean()

Возвращает true, если term является целым числом или числом с плавающей точкой; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_pid(term)

is_pid(term()) :: boolean()

Возвращает true если term это PID (идентификатор процесса); в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_port(term)

is_port(term()) :: boolean()

Возвращает true если term это идентификатор порта; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_reference(term)

is_reference(term()) :: boolean()

Возвращает true если term это ссылка; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

is_tuple(term)

is_tuple(term()) :: boolean()

Возвращает true если term является кортежем; в противном случае возвращает false.

Разрешено в тестах охранных условий. Встраивается компилятором.

length(list)

length(list()) :: non_neg_integer()

Возвращает длину list.

Разрешено в тестах охранных условий. Встраивается компилятором.

Примеры

iex> length([1, 2, 3, 4, 5, 6, 7, 8, 9])
9

macro_exported?(module, macro, arity)

macro_exported?(module(), atom(), arity()) :: boolean()

Возвращает true если module загружен и содержит общедоступный macro с заданным arity, в противном случае false.

Обратите внимание, что эта функция не загружает модуль в случае, если он не загружен. См. Code.ensure_loaded/1 для получения дополнительной информации.

Если module является модулем Erlang (в отличие от модуля Elixir), эта функция всегда возвращает false.

Примеры

iex> macro_exported?(Kernel, :use, 2)
true

iex> macro_exported?(:erlang, :abs, 1)
false

make_ref()

make_ref() :: reference()

Возвращает почти уникальную ссылку.

Возвращаемая ссылка появится снова примерно после 2^82 вызовов; поэтому она достаточно уникальна для практических целей.

Встраивается компилятором.

Примеры

make_ref() #=> #Reference<0.0.0.135>

map_size(map)

map_size(map()) :: non_neg_integer()

Возвращает размер карты.

Размер карты — это количество пар ключ-значение, содержащихся в карте.

Эта операция выполняется за постоянное время.

Разрешено в тестах охранных условий. Встраивается компилятором.

Примеры

iex> map_size(%{a: "foo", b: "bar"})
2

match?(pattern, expr) (макрос)

Удобный макрос, который проверяет, соответствует ли правая сторона (выражение) левой стороне (шаблону).

Примеры

iex> match?(1, 1)
true

iex> match?(1, 2)
false

iex> match?({1, _}, {1, 2})
true

iex> map = %{a: 1, b: 2}
iex> match?(%{a: _}, map)
true

iex> a = 1
iex> match?(^a, 1)
true

match?/2 очень полезен при фильтрации или поиске значения в перечислимом объекте:

iex> list = [a: 1, b: 2, a: 3]
iex> Enum.filter(list, &match?({:a, _}, &1))
[a: 1, a: 3]

Охранные условия также могут быть заданы для соответствия:

iex> list = [a: 1, b: 2, a: 3]
iex> Enum.filter(list, &match?({:a, x} when x < 2, &1))
[a: 1]

Однако переменные, присвоенные в соответствии, не будут доступны за пределами вызова функции (в отличие от обычного сопоставления с шаблоном с оператором =):

iex> match?(_x, 1)
true
iex> binding()
[]

max(first, second)

max(first, second) :: first | second when first: term(), second: term()

Возвращает наибольшее из двух заданных терминов в соответствии с порядком Erlang.

Если термины равны, возвращается первый.

Встраивается компилятором.

Примеры

iex> max(1, 2)
2
iex> max(:a, :b)
:b

Использование порядка Erlang означает, что сравнения структурные, а не семантические. Например, при сравнении дат:

iex> max(~D[2017-03-31], ~D[2017-04-01])
~D[2017-03-31]

В примере выше, max/1 возвратил 31 марта, а не 1 апреля, потому что структурное сравнение сравнивает день до года. В таких случаях обычно модули предоставляют функции, такие как Date.compare/1, которые выполняют семантическое сравнение.

min(first, second)

min(first, second) :: first | second when first: term(), second: term()

Возвращает наименьшее из двух заданных терминов в соответствии с порядком Erlang.

Если термины равны, возвращается первый.

Встраивается компилятором.

Примеры

iex> min(1, 2)
1
iex> min("foo", "bar")
"bar"

Использование порядка Erlang означает, что сравнения структурные, а не семантические. Например, при сравнении дат:

iex> min(~D[2017-03-31], ~D[2017-04-01])
~D[2017-04-01]

В примере выше, min/1 вернул 1 апреля, а не 31 марта, потому что структурное сравнение сравнивает день до года. В таких случаях обычно модули предоставляют функции, такие как Date.compare/1, которые выполняют семантическое сравнение.

node()

node() :: node()

Возвращает атом, представляющий имя локального узла. Если узел не активен, возвращается :nonode@nohost.

Разрешено в тестах охранных условий. Встраивается компилятором.

node(arg)

node(pid() | reference() | port()) :: node()

Возвращает узел, где находится заданный аргумент. Аргумент может быть PID, ссылкой или портом. Если локальный узел не активен, возвращается :nonode@nohost.

Разрешено в тестах охранных условий. Встраивается компилятором.

not(value)

not false :: true
not true :: false

Логическое отрицание.

arg должно быть булевым значением; если это не так, генерируется исключение ArgumentError.

Разрешено в тестах охранных условий. Встраивается компилятором.

Примеры

iex> not false
true

left or right (макрос)

Логическое ИЛИ.

Если left есть true, возвращает true; в противном случае возвращает right.

Требуется только left операнд должен быть булевым, так как он выполняет короткое замыкание. Если left операнд не булевый, генерируется исключение ArgumentError.

Разрешено в тестах охранных условий.

Примеры

iex> true or false
true
iex> false or 42
42

pop_in(path) (макрос)

Извлекает ключ из вложенной структуры по заданному path.

Это аналогично pop_in/2, за исключением того, что путь извлекается через макрос, а не в виде списка. Например:

pop_in(opts[:foo][:bar])

Эквивалентно:

pop_in(opts, [:foo, :bar])

Обратите внимание, что для работы этого макроса весь путь должен быть всегда виден этому макросу. Для получения дополнительной информации о поддерживаемых выражениях путей, см. get_and_update_in/2 документацию.

Примеры

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users["john"][:age])
{27, %{"john" => %{}, "meg" => %{age: 23}}}

iex> users = %{john: %{age: 27}, meg: %{age: 23}}
iex> pop_in(users.john[:age])
{27, %{john: %{}, meg: %{age: 23}}}

В случае, если какое-либо значение возвращает nil, ключ будет удален, и удаление будет считаться успешным.

pop_in(data, keys)

pop_in(data, [Access.get_and_update_fun(term(), data) | term(), ...]) ::
  {term(), data}
when data: Access.container()

Извлекает ключ из заданной вложенной структуры.

Использует протокол Access для обхода структур в соответствии с заданным keys, если key не является функцией. Если ключ является функцией, она будет вызвана в соответствии с get_and_update_in/3.

Примеры

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users, ["john", :age])
{27, %{"john" => %{}, "meg" => %{age: 23}}}

В случае, если какой-либо элемент возвращает nil, его ключ будет удален, и удаление будет считаться успешным.

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> pop_in(users, ["jane", :age])
{nil, %{"john" => %{age: 27}, "meg" => %{age: 23}}}

put_elem(tuple, index, value)

put_elem(tuple(), non_neg_integer(), term()) :: tuple()

Размещает value по указанному индексу (с нуля) в tuple.

Встраивается компилятором.

Примеры

iex> tuple = {:foo, :bar, 3}
iex> put_elem(tuple, 0, :baz)
{:baz, :bar, 3}

put_in(path, value) (макрос)

Размещает значение во вложенной структуре по заданному пути.

Аналогично put_in/3, за исключением того, что путь извлекается с помощью макроса, а не передаётся в виде списка. Например:

put_in(opts[:foo][:bar], :baz)

Эквивалентно:

put_in(opts, [:foo, :bar], :baz)

Обратите внимание, что для работы этого макроса весь путь должен быть виден этому макросу. Более подробную информацию о поддерживаемых выражениях путей можно найти в документации get_and_update_in/2.

Примеры

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"][:age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users["john"].age, 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}

put_in(data, keys, value)

put_in(Access.t(), [term(), ...], term()) :: Access.t()

Размещает значение во вложенной структуре.

Использует модуль Access для обхода структур в соответствии с заданными keys, если key не является функцией. Если ключ является функцией, она будет вызвана в соответствии с get_and_update_in/3.

Примеры

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users, ["john", :age], 28)
%{"john" => %{age: 28}, "meg" => %{age: 23}}

В случае, если любой из промежуточных элементов возвращает nil, при попытке последующего доступа возникнет ошибка.

raise(message) (макрос)

Вызывает исключение.

Если аргумент msg является бинарным, вызывается исключение RuntimeError с заданным сообщением.

Если msg — атом, вызывается raise/2 с атомом в качестве первого аргумента и [] во втором.

Если msg — структура исключения, она вызывается как есть.

Если msg — что-либо ещё, raise завершится с исключением ArgumentError.

Примеры

iex> raise "oops"
** (RuntimeError) oops

try do
  1 + :foo
rescue
  x in [ArithmeticError] ->
    IO.puts "that was expected"
    raise x
end

raise(exception, attributes) (макрос)

Вызывает исключение.

Вызывает функцию exception/1 для заданного аргумента (который должен быть именем модуля, как ArgumentError или RuntimeError), передавая attrs в качестве атрибутов для получения структуры исключения.

Любой модуль, содержащий вызов макроса defexception/1, автоматически реализует обратный вызов Exception.exception/1, ожидаемый raise/2. Для получения дополнительной информации см. defexception/1.

Примеры

iex> raise(ArgumentError, "Sample")
** (ArgumentError) Sample

rem(dividend, divisor)

rem(integer(), neg_integer() | pos_integer()) :: integer()

Вычисляет остаток от деления целых чисел.

rem/2 использует усечённое деление, что означает, что результат всегда имеет знак dividend.

Вызывает исключение ArithmeticError, если один из аргументов не является целым числом или если divisor равно 0.

Разрешено в тестах с проверкой условий. Встраивается компилятором.

Примеры

iex> rem(5, 2)
1
iex> rem(6, -4)
2

reraise(message, stacktrace) (макрос)

Вызывает исключение, сохраняя предыдущий стек вызовов.

Работает как raise/1, но не создаёт новый стек вызовов.

Обратите внимание, что __STACKTRACE__ можно использовать внутри блока catch/rescue для получения текущего стека вызовов.

Примеры

try do
  raise "oops"
rescue
  exception ->
    reraise exception, __STACKTRACE__
end

reraise(exception, attributes, stacktrace) (макрос)

Вызывает исключение, сохраняя предыдущий стек вызовов.

reraise/3 работает как reraise/2, но передаёт аргументы в функцию exception/1 как описано в raise/2.

Примеры

try do
  raise "oops"
rescue
  exception ->
    reraise WrapperError, [exception: exception], __STACKTRACE__
end

round(number)

round(value) :: value when value: integer()
round(float()) :: integer()

Округляет число до ближайшего целого.

Разрешено в тестах с проверкой условий. Встраивается компилятором.

Примеры

iex> round(5.6)
6

iex> round(5.2)
5

iex> round(-9.9)
-10

iex> round(-9)
-9

self()

self() :: pid()

Возвращает PID (идентификатор процесса) вызывающего процесса.

Разрешено в проверке условий. Встраивается компилятором.

send(dest, message)

send(dest :: Process.dest(), message) :: message when message: any()

Отправляет сообщение заданному dest и возвращает сообщение.

dest может быть удалённым или локальным PID, локальным портом, локально зарегистрированным именем или кортежем в формате {registered_name, node} для зарегистрированного имени на другом узле.

Встраивается компилятором.

Примеры

iex> send(self(), :hello)
:hello

sigil_C(term, modifiers) (макрос)

Обрабатывает сигил ~C для списков символов.

Просто возвращает список символов без экранирования символов и без интерполяций.

Примеры

iex> ~C(foo)
'foo'

iex> ~C(f#{o}o)
'f\#{o}o'

sigil_D(date, modifiers) (макрос)

Обрабатывает сигил ~D для дат.

Вариант с маленькой буквой ~d не существует, так как интерполяция и экранирование символов бесполезны для сигилов дат.

Дополнительную информацию о датах можно найти в модуле Date.

Примеры

iex> ~D[2015-01-13]
~D[2015-01-13]

sigil_N(date, modifiers) (макрос)

Обрабатывает сигил ~N для дат без учёта часового пояса.

Вариант с маленькой буквой ~n не существует, так как интерполяция и экранирование символов бесполезны для сигилов дат без учёта часового пояса.

Дополнительную информацию о датах без учёта часового пояса можно найти в модуле NaiveDateTime.

Примеры

iex> ~N[2015-01-13 13:00:07]
~N[2015-01-13 13:00:07]
iex> ~N[2015-01-13T13:00:07.001]
~N[2015-01-13 13:00:07.001]

sigil_R(term, modifiers) (макрос)

Обрабатывает сигил ~R для регулярных выражений.

Возвращает шаблон регулярного выражения без экранирования и интерпретации интерполяций.

Дополнительную информацию о регулярных выражениях можно найти в модуле Regex.

Примеры

iex> Regex.match?(~R(f#{1,3}o), "f#o")
true

sigil_S(term, modifiers) (макрос)

Обрабатывает сигил ~S для строк.

Просто возвращает строку без экранирования символов и без интерполяций.

Примеры

iex> ~S(foo)
"foo"

iex> ~S(f#{o}o)
"f\#{o}o"

sigil_T(date, modifiers) (макрос)

Обрабатывает сигил ~T для времени.

Вариант с маленькой буквой ~t не существует, так как интерполяция и экранирование символов бесполезны для сигилов времени.

Дополнительную информацию о времени можно найти в модуле Time.

Примеры

iex> ~T[13:00:07]
~T[13:00:07]
iex> ~T[13:00:07.001]
~T[13:00:07.001]

sigil_W(term, modifiers) (макрос)

Обрабатывает сигил ~W для списка слов.

Возвращает список «слов», разделённых пробелами, без экранирования и интерпретации интерполяций.

Модификаторы

  • s: слова в списке являются строками (по умолчанию)
  • a: слова в списке являются атомами
  • c: слова в списке являются списками символов

Примеры

iex> ~W(foo #{bar} baz)
["foo", "\#{bar}", "baz"]

sigil_c(term, modifiers) (макрос)

Обрабатывает сигил ~c для списков символов.

Возвращает список символов как одиночную строку в одинарных кавычках, без экранирования символов и с заменой интерполяций.

Примеры

iex> ~c(foo)
'foo'

iex> ~c(f#{:o}o)
'foo'

iex> ~c(f\#{:o}o)
'f\#{:o}o'

sigil_r(term, modifiers) (макрос)

Обрабатывает сигил ~r для регулярных выражений.

Возвращает шаблон регулярного выражения, без экранирования символов и с заменой интерполяций.

Дополнительную информацию о регулярных выражениях можно найти в модуле Regex.

Примеры

iex> Regex.match?(~r(foo), "foo")
true

iex> Regex.match?(~r/abc/, "abc")
true

sigil_s(term, modifiers) (макрос)

Обрабатывает сигил ~s для строк.

Возвращает строку так, как если бы она была строкой в двойных кавычках, при этом происходит удаление экранирования символов и замена интерполяций.

Примеры

iex> ~s(foo)
"foo"

iex> ~s(f#{:o}o)
"foo"

iex> ~s(f\#{:o}o)
"f\#{:o}o"

sigil_w(term, modifiers) (макрос)

Обрабатывает сигил ~w для списка слов.

Возвращает список «слов», разделённых пробелами. Для каждого слова происходит удаление экранирования символов и замена интерполяций.

Модификаторы

  • s: слова в списке являются строками (по умолчанию)
  • a: слова в списке являются атомами
  • c: слова в списке являются списками символов

Примеры

iex> ~w(foo #{:bar} baz)
["foo", "bar", "baz"]

iex> ~w(foo #{" bar baz "})
["foo", "bar", "baz"]

iex> ~w(--source test/enum_test.exs)
["--source", "test/enum_test.exs"]

iex> ~w(foo bar baz)a
[:foo, :bar, :baz]

spawn(fun)

spawn((() -> any())) :: pid()

Запускает заданную функцию и возвращает её PID.

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

См. модуль Process для получения дополнительных функций, связанных с процессами.

Анонимная функция принимает 0 аргументов и может вернуть любое значение.

Встраивается компилятором.

Примеры

current = self()
child = spawn(fn -> send current, {self(), 1 + 2} end)

receive do
  {^child, 3} -> IO.puts "Received 3 back"
end

spawn(module, fun, args)

spawn(module(), atom(), list()) :: pid()

Запускает заданную функцию fun из данного module, передавая ей заданные args и возвращает её PID.

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

См. модуль Process для получения дополнительных функций, связанных с процессами.

Встраивается компилятором.

Примеры

spawn(SomeModule, :function, [1, 2, 3])

spawn_link(fun)

spawn_link((() -> any())) :: pid()

Запускает заданную функцию, связывает её с текущим процессом и возвращает её PID.

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

См. модуль Process для получения дополнительных функций, связанных с процессами. Для получения дополнительной информации о связывании, см. Process.link/1.

Анонимная функция принимает 0 аргументов и может вернуть любое значение.

Встраивается компилятором.

Примеры

current = self()
child = spawn_link(fn -> send(current, {self(), 1 + 2}) end)

receive do
  {^child, 3} -> IO.puts "Received 3 back"
end

spawn_link(module, fun, args)

spawn_link(module(), atom(), list()) :: pid()

Запускает заданную функцию fun из данного module, передавая ей заданные args, связывает её с текущим процессом и возвращает её PID.

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

См. модуль Process для получения дополнительных функций, связанных с процессами. Для получения дополнительной информации о связывании, см. Process.link/1.

Встраивается компилятором.

Примеры

spawn_link(SomeModule, :function, [1, 2, 3])

spawn_monitor(fun)

spawn_monitor((() -> any())) :: {pid(), reference()}

Запускает заданную функцию, отслеживает её и возвращает её PID и ссылку на отслеживание.

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

См. модуль Process для получения дополнительных функций, связанных с процессами.

Анонимная функция принимает 0 аргументов и может вернуть любое значение.

Встраивается компилятором.

Примеры

current = self()
spawn_monitor(fn -> send current, {self(), 1 + 2} end)

spawn_monitor(module, fun, args)

spawn_monitor(module(), atom(), list()) :: {pid(), reference()}

Запускает указанный модуль и функцию, передавая указанные аргументы, отслеживает её и возвращает её PID и ссылку на отслеживание.

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

См. модуль Process для получения дополнительных функций, связанных с процессами.

Встраивается компилятором.

Примеры

spawn_monitor(SomeModule, :function, [1, 2, 3])

struct!(struct, fields \\ [])

struct!(module() | struct(), Enum.t()) :: struct() | no_return()

Аналогично struct/2, но проверяет валидность ключей.

Функция struct!/2 эмулирует поведение структур на этапе компиляции. Это означает, что:

  • при создании структуры, как в struct!(SomeStruct, key: :value), она эквивалентна %SomeStruct{key: :value}, и поэтому эта функция проверит, принадлежит ли каждая пара ключ-значение структуре. Если структура требует какой-либо ключ через @enforce_keys, то они также будут проверены;

  • при обновлении структуры, как в struct!(%SomeStruct{}, key: :value), она эквивалентна %SomeStruct{struct | key: :value}, и поэтому эта функция проверит, принадлежит ли каждая пара ключ-значение структуре. Однако обновление структур не требует проверки ключей, так как ключи проверяются только при создании;

struct(struct, fields \\ [])

struct(module() | struct(), Enum.t()) :: struct()

Создаёт и обновляет структуры.

Аргумент struct может быть атомом (который определяет defstruct) или самой структурой. Второй аргумент — любой Enumerable, который генерирует пары ключ-значение во время перечисления.

Ключи в Enumerable, которые не существуют в структуре, автоматически отбрасываются. Обратите внимание, что ключи должны быть атомами, так как только атомы разрешены при определении структуры.

Эта функция полезна для динамического создания и обновления структур, а также для преобразования карт в структуры; в последнем случае просто добавление соответствующего поля :__struct__ в карту может быть недостаточно, и следует использовать struct/2 вместо этого.

Примеры

defmodule User do
  defstruct name: "john"
end

struct(User)
#=> %User{name: "john"}

opts = [name: "meg"]
user = struct(User, opts)
#=> %User{name: "meg"}

struct(user, unknown: "value")
#=> %User{name: "meg"}

struct(User, %{name: "meg"})
#=> %User{name: "meg"}

# String keys are ignored
struct(User, %{"name" => "meg"})
#=> %User{name: "john"}

throw(term)

throw(term()) :: no_return()

Нелокальное возвращение из функции.

См. Kernel.SpecialForms.try/1 для получения дополнительной информации.

Встраивается компилятором.

tl(list)

tl(nonempty_maybe_improper_list(elem, tail)) ::
  maybe_improper_list(elem, tail) | tail
when elem: term(), tail: term()

Возвращает хвост списка. Вызывает ArgumentError, если список пуст.

Работает со списками неправильной формы.

Разрешено в тестах стражей. Встраивается компилятором.

Примеры

tl([1, 2, 3, :go])
#=> [2, 3, :go]

tl([])
#=> ** (ArgumentError) argument error

tl([:one])
#=> []

tl([:a, :b | :c])
#=> [:b | :c]

tl([:a | %{b: 1}])
#=> %{b: 1}

to_charlist(term) (макрос)

Преобразует заданный термин в список символов в соответствии с протоколом List.Chars.

Примеры

iex> to_charlist(:foo)
'foo'

to_string(term) (макрос)

Преобразует аргумент в строку в соответствии с протоколом String.Chars.

Это функция, вызываемая при интерполяции строк.

Примеры

iex> to_string(:foo)
"foo"

trunc(number)

trunc(float()) :: integer()
trunc(value) :: value when value: integer()

Возвращает целую часть number.

Разрешено в тестах стражей. Встраивается компилятором.

Примеры

iex> trunc(5.4)
5

iex> trunc(-5.99)
-5

iex> trunc(-5)
-5

tuple_size(tuple)

tuple_size(tuple()) :: non_neg_integer()

Возвращает размер кортежа.

Эта операция выполняется за константное время.

Разрешено в тестах стражей. Встраивается компилятором.

Примеры

iex> tuple_size({:a, :b, :c})
3

unless(condition, clauses) (макрос)

Предоставляет макрос unless.

Этот макрос вычисляет и возвращает блок do, переданный во втором аргументе, если clause вычисляется не как true. В противном случае возвращает значение блока else, если он есть, или nil, если нет.

См. также if/2.

END_OF_DOCUMENT_MARKER

Примеры

iex> unless(Enum.empty?([]), do: "Hello")
nil

iex> unless(Enum.empty?([1, 2, 3]), do: "Hello")
"Hello"

iex> unless Enum.sum([2, 2]) == 5 do
...>   "Math still works"
...> else
...>   "Math is broken"
...> end
"Math still works"

update_in(path, fun) (макрос)

Обновляет вложенную структуру с помощью заданной path.

Это аналогично update_in/3, за исключением того, что путь извлекается через макрос, а не передается в виде списка. Например:

update_in(opts[:foo][:bar], &(&1 + 1))

Эквивалентно:

update_in(opts, [:foo, :bar], &(&1 + 1))

Обратите внимание, что для работы этого макроса весь путь должен быть всегда виден этим макросом. Дополнительную информацию о поддерживаемых выражениях пути см. в документации get_and_update_in/2.

Примеры

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users["john"][:age], &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users["john"].age, &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}

update_in(data, keys, fun)

update_in(Access.t(), [term(), ...], (term() -> term())) :: Access.t()

Обновляет ключ во вложенной структуре.

Использует модуль Access для обхода структур в соответствии с заданным keys, если key не является функцией. Если ключ является функцией, она вызывается, как указано в get_and_update_in/3.

Примеры

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users, ["john", :age], &(&1 + 1))
%{"john" => %{age: 28}, "meg" => %{age: 23}}

В случае, если какой-либо из элементов в середине возвращает nil, при попытке последующего доступа будет произойти ошибка.

use(module, opts \\ []) (макрос)

Использует указанный модуль в текущем контексте.

При вызове:

use MyModule, some: :options

макрос __using__/1 из модуля MyModule вызывается со вторым аргументом, переданным в use, в качестве своего аргумента. Поскольку __using__/1 является макросом, применяются все обычные правила макросов, а его возвращаемое значение должно быть цитируемым кодом, который затем вставляется туда, где вызван use/2.

Примеры

Например, для написания тестовых случаев с использованием фреймворка ExUnit, предоставленного с Elixir, разработчик должен use модуль ExUnit.Case:

defmodule AssertionTest do
  use ExUnit.Case, async: true

  test "always pass" do
    assert true
  end
end

В этом примере ExUnit.Case.__using__/1 вызывается со списком ключевых слов [async: true] в качестве аргумента; use/2 преобразуется в:

defmodule AssertionTest do
  require ExUnit.Case
  ExUnit.Case.__using__([async: true])

  test "always pass" do
    assert true
  end
end

ExUnit.Case затем определит макрос __using__/1:

defmodule ExUnit.Case do
  defmacro __using__(opts) do
    # do something with opts
    quote do
      # return some code to inject in the caller
    end
  end
end

Рекомендации по использованию

__using__/1 обычно используется, когда требуется установить некоторое состояние (через атрибуты модуля) или обратные вызовы (например, @before_compile, см. документацию по Module для получения дополнительной информации) в вызывающую сторону.

__using__/1 также может использоваться для алиасинга, требования или импорта функциональности из разных модулей:

defmodule MyModule do
  defmacro __using__(_opts) do
    quote do
      import MyModule.Foo
      import MyModule.Bar
      import MyModule.Baz

      alias MyModule.Repo
    end
  end
end

Однако, не предоставляйте __using__/1 если всё, что он делает, это импорт, алиасинг или требование самого модуля. Например, избегайте этого:

defmodule MyModule do
  defmacro __using__(_opts) do
    quote do
      import MyModule
    end
  end
end

В таких случаях разработчики должны вместо этого импортировать или алиасить модуль напрямую, чтобы они могли настроить его по своему желанию без косвенности, стоящей за use/2.

Наконец, разработчики также должны избегать определения функций внутри обратного вызова __using__/1, за исключением тех случаев, когда эти функции являются реализацией по умолчанию ранее определенного @callback или являются функциями, предназначенными для переопределения (см. defoverridable/1). Даже в этих случаях определение функций следует рассматривать как «последний вариант».

Если вы хотите предоставить пользователю некоторую существующую функциональность, определите её в модуле, который будет соответствующим образом импортирован; например, ExUnit.Case не определяет макрос test/3 в модуле, который вызывает use ExUnit.Case, но определяет ExUnit.Case.test/3 и просто импортирует его в вызывающую сторону при использовании.

var!(var, context \\ nil) (макрос)

При использовании внутри цитирования отмечает, что заданная переменная не должна подвергаться гигиенизации.

Аргумент может быть либо нецитируемой переменной, либо в стандартной форме кортежа {name, meta, context}.

См. Kernel.SpecialForms.quote/2 для получения дополнительной информации.

left |> right (макрос)

Оператор конвейера.

Этот оператор представляет выражение слева в качестве первого аргумента вызова функции справа.

Примеры

iex> [1, [2], 3] |> List.flatten()
[1, 2, 3]

Приведённый выше пример эквивалентен вызову List.flatten([1, [2], 3]).

Оператор |> в основном полезен, когда нужно выполнить серию операций, напоминающих конвейер:

iex> [1, [2], 3] |> List.flatten() |> Enum.map(fn x -> x * 2 end)
[2, 4, 6]

В примере выше список [1, [2], 3] передается в качестве первого аргумента функции List.flatten/1, затем уплощённый список передается в качестве первого аргумента функции Enum.map/2, которая удваивает каждый элемент списка.

Другими словами, выражение выше просто переводится в:

Enum.map(List.flatten([1, [2], 3]), fn x -> x * 2 end)

Возможные ошибки

Существует две распространённые ошибки при использовании оператора конвейера.

Первая связана с приоритетом операторов. Например, следующее выражение:

String.graphemes "Hello" |> Enum.reverse

Переводится в:

String.graphemes("Hello" |> Enum.reverse)

что приводит к ошибке, так как протокол Enumerable не определён для двоичных данных. Добавление явных скобок устраняет неоднозначность:

String.graphemes("Hello") |> Enum.reverse

Или, ещё лучше:

"Hello" |> String.graphemes |> Enum.reverse

Вторая ошибка заключается в том, что оператор |> работает с вызовами. Например, когда вы пишете:

"Hello" |> some_function()

Elixir видит, что правая часть является вызовом функции и направляет к нему конвейер. Это означает, что если вы хотите направить в анонимную или захваченную функцию, она также должна быть явно вызвана.

Учитывая анонимную функцию:

fun = fn x -> IO.puts(x) end
fun.("Hello")

Это не сработает, так как вместо этого будет попытка вызвать локальную функцию fun:

"Hello" |> fun()

Это работает:

"Hello" |> fun.()

Как вы можете видеть, оператор |> сохраняет те же семантику, что и при отсутствии конвейера, так как оба требуют обозначения fun.(...).

left || right (макрос)

Предоставляет оператор короткого замыкания, который вычисляет и возвращает второе выражение только в том случае, если первое не вычисляется до true (т.е., оно либо nil или false). В противном случае возвращает первое выражение.

Не разрешается в условиях защиты.

Примеры

iex> Enum.empty?([1]) || Enum.empty?([1])
false

iex> List.first([]) || true
true

iex> Enum.empty?([1]) || 1
1

iex> Enum.empty?([]) || throw(:bad)
true

Обратите внимание, что, в отличие от or/2, этот оператор принимает любое выражение в качестве первого аргумента, а не только булевы значения.

© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.7.4/Kernel.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API