Spec-Zone.ru › Elixir 1.6

Ядро

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

Он предоставляет по умолчанию функции и макросы, которые Elixir импортирует в вашу среду. Их можно напрямую вызывать в вашем коде без имени модуля:

iex> is_number(13)
true

Эти функции и макросы могут быть пропущены или выбраны вручную с помощью макроса Kernel.SpecialForms.import/2. Например, если вы хотите указать Elixir не импортировать макрос if/2, вы можете сделать так:

import Kernel, except: [if: 2]

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

Этот модуль предоставляет различные возможности для определения кода, управления потоком и работы с типами данных. Например, новые процессы можно создать с помощью spawn/1, модули можно определить с помощью defmodule/2, операторы короткого замыкания находятся в &&/2 и ||/2, числа можно складывать с помощью +/2, и многое другое. Этот модуль также содержит все встроенные гварды, которые представляют собой набор функций, дополняющих сопоставление с образцом сложными проверками. Более подробная информация о гвардах находится на странице Страница гвардов.

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

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

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

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

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

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

  • Битовые строки - последовательность битов, созданных с помощью Kernel.SpecialForms.<<>>/1. Когда количество битов кратно 8, они называются бинарными и могут быть обработаны с помощью модуля :binary Erlang
  • Функция - ссылка на фрагмент кода, созданный с помощью специальной формы Kernel.SpecialForms.fn/2
  • Ссылка - уникальное значение в системе выполнения, созданное с помощью 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
  • Операторы - перечисляет все операторы Elixir и их приоритет
  • Справочник по синтаксису - справочник по синтаксису языка
  • Синтаксис Юникод - обзор поддержки Elixir для Юникода
  • Написание документации - рекомендации по написанию документации в Elixir.

Встраивание

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

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

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

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

Сводка

Функции

!value

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

left != right

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

left !== right

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

left && right

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

left * right

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

+value

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

left + right

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

left ++ right

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

-value

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

left - right

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

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, fun, args)

Вызывает данную fun из 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!(struct, fields \\ [])

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

throw(term)

Нелокальное возвращение из функции

tl(list)

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

to_charlist(term)
END_OF_DOCUMENT_MARKER

Преобразует заданный термин в список символов в соответствии с протоколом 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

+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() ++ term() :: maybe_improper_list()

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

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

Если операнд right не является правильным списком, он возвращает неправильный список. Если операнд left не является правильным списком, он генерирует 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

-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

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]

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"

Оператор <> также может использоваться в шаблонах совпадения (и условиях охраны), если первая часть — это буквальная бинарная строка:

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

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

left == right

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

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

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

Все термины в 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 (макрос)

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

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

Требуется только первый аргумент быть булевым, поскольку он выполняется по короткой схеме. Если первый аргумент не является булевым, генерируется исключение 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, fun, args)

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

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

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

Примеры

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 - получает структуру исключения и должна вернуть её сообщение. Чаще всего у исключений есть поле message, к которому по умолчанию обращается эта функция. Однако, если у исключения нет поля message, эта функция должна быть явно реализована.

Поскольку исключения являются структурами, доступен 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]

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

raise MyAppError, value

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

defguard(guard) (макрос)

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) (макрос)

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 — это поведенческая модель по выбору. Для большинства протоколов поднятие ошибки при отсутствии реализации протокола является правильным поведением.

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

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

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). Если да, то обратный вызов вызывается; в противном случае реализация, которая просто ссылается на любую реализацию, автоматически наследуется.

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

При создании структуры 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.

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

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, одновременно сигнализируя всем связанным процессам OTP о вежливом завершении.

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

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 отсутствует) и должен возвращать кортеж из двух элементов: значение «получить» (полученное значение, которое может быть обработано перед возвратом) и новое значение для хранения под key. Аргумент fun также может вернуть :pop, что подразумевает, что текущее значение должно быть удалено из структуры и возвращено.

Он использует модуль 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 ->
...>   Enum.map(data, next) |> :lists.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 в соответствии с модулем доступа:

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 (макрос)

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

Если первый аргумент — true, возвращается true; в противном случае — второй аргумент.

Требуется только первый аргумент для того, чтобы быть булевым, так как он реализует короткое замыкание. Если первый аргумент не является булевым, выбрасывается исключение 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, её ключ будет удалён, и удаление будет считаться успешным.

put_elem(tuple, index, value)

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

Помещает value в заданный нулевой индекс index в tuple.

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

Примеры

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

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

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

Это аналогично 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, message: "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, но не генерирует новый стек вызовов.

Обратите внимание, что System.stacktrace/0 возвращает стек вызовов последнего исключения. В связи с этим, часто стек вызовов присваивается как первое выражение в блоке rescue, так как любое другое потенциально вызванное (и перехваченное) исключение между блоком catch и вызовом raise может изменить значение System.stacktrace/0.

Примеры

try do
  raise "oops"
rescue
  exception ->
    stacktrace = System.stacktrace
    if Exception.message(exception) == "oops" do
      reraise exception, stacktrace
    end
end

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

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

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

Примеры

try do
  raise "oops"
rescue
  exception ->
    stacktrace = System.stacktrace
    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 :: pid() | port() | atom() | {atom(), node()}, 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 не существует, так как интерполяция и экранирование символов не нужны для сигилов дат.

Примеры

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

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

Обрабатывает сигил ~N для даты-времени по умолчанию.

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

Примеры

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 не существует, так как интерполяция и экранирование символов не нужны для сигилов времени.

Примеры

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: слова в списке — списки символов
END_OF_DOCUMENT_MARKER

Примеры

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()

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

Аргумент 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"}

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}, и поэтому эта функция проверит, принадлежит ли каждая пара ключ-значение структуре. Однако обновление структур не проверяет ключи, поскольку ключи проверяются только при создании;

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.

Примеры

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, разработчик должен использовать модуль 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.6.6/Kernel.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API