Spec-Zone.ru › Elixir 1.3

Ядро

Предоставляет по умолчанию макросы и функции, которые Elixir импортирует в вашу среду.

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

import Kernel, except: [if: 2]

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

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

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

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

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

Краткое описание

Функции

!arg

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

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

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

left / right

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

left < right

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

left <= right

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

left <> right

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

left == right

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

left === right

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

left =~ right

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

left > right

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

left >= right

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

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

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

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

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

defmacro(call, expr \\ nil)

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

defmacrop(call, expr \\ nil)

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

defmodule(alias, list)

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

defoverridable(keywords)

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

defp(call, expr \\ nil)

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

defprotocol(name, list)

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

defstruct(fields)

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

destructure(left, right)

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

div(left, right)

Производит целочисленное деление

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(arg, 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 arg

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

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

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

raise(exception, attrs)

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

rem(left, right)

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

reraise(msg, stacktrace)

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

reraise(exception, attrs, stacktrace)

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

round(number)

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

self()

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

send(dest, msg)

Отправляет сообщение заданному 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)

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

spawn_link(fun)

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

spawn_link(module, fun, args)

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

spawn_monitor(fun)

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

spawn_monitor(module, fun, args)

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

struct(struct, kv \\ [])

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

struct!(struct, kv \\ [])

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

throw(term)

Нелокальное возвращение из функции. Подробнее см. Kernel.SpecialForms.try/1

tl(list)

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

to_charlist(arg)

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

to_string(arg)

Преобразует аргумент в строку в соответствии с протоколом 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). В противном случае возвращает первое выражение.

Функции

!arg (макрос)

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

Принимает любой аргумент (не только булевы) и возвращает 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

number * number :: number

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

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

Примеры

iex> 1 * 2
2

+value

+number :: number

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

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

Примеры

iex> +1
1

left + right

number + number :: number

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

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

Примеры

iex> 1 + 2
3

left ++ right

list ++ term :: maybe_improper_list

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

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

Вместо этого рассмотрите добавление в начало с помощью [item | rest] и последующего реверсирования.

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

Примеры

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

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

-value

-number :: number

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

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

Примеры

iex> -2
-2

left - right

number - number :: number

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

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

Примеры

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

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

Оба конца включены.

Примеры

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, если вам нужно целочисленное деление или остаток.

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

Примеры

iex> 1 / 2
0.5

iex> 2 / 1
2.0

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 равны, но не совпадают.

Все термины в 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-поведение под названием gen_server:

defmodule MyServer do
  @behaviour :gen_server
  # ... 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, pos_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; эта функция принимает два аргумента и возвращает их сумму.

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

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

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

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

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

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

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

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

Параметры

  • :to — выражение, которому следует делегировать. Разрешены любые выражения, результаты которых будут вычислены во время выполнения. Обычно оно оценивается до имени модуля.

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

  • :append_first — если true, при делегировании первый аргумент, переданный делегированной функции, будет перемещён в конец аргументов при передаче в целевой модуль.

    Это сделано для того, чтобы Elixir нормализовал «обработчик» как первый аргумент, в то время как некоторые модули Erlang ожидают его как последний аргумент.

Примеры

defmodule MyList do
  defdelegate reverse(list), to: :lists
  defdelegate other_reverse(list), to: :lists, as: :reverse
  defdelegate [reverse(list), map(list, callback)], to: :lists, append_first: true
end

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

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

MyList.map([1, 2, 3], &(&1 * 2))
#=> [2, 4, 6]

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

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

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

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

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

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

Возбуждение исключений

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

defmodule MyAppError do
  defexception [:message]
end

value = [:hello]

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

Во многих случаях удобнее передать ожидаемое значение в raise/2 и сгенерировать сообщение в обратном вызове exception/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

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

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

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

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

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

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

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

Примеры

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 для получения дополнительной информации.

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

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

Этот макрос определяет модуль с заданным 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) (макрос)

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

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

Пример

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

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

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

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

Примеры

В Elixir только false и nil считаются ложными значениями. Всё остальное вычисляется как true в if/2 условиях. В зависимости от приложения, может быть важно указать blank? протокол, возвращающий булево значение для других типов данных, которые должны рассматриваться как «пустые». Например, пустой список или пустой бинарник могут считаться пустыми.

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

defprotocol Blank do
  @doc "Returns `true` if `data` is considered blank/empty"
  def blank?(data)
end

Теперь, когда протокол определен, его можно реализовать. Он должен быть реализован для каждого типа Elixir, например:

# Integers are never blank
defimpl Blank, for: Integer do
  def blank?(number), do: false
end

# The only blank list is the empty one
defimpl Blank, for: List do
  def blank?([]), do: true
  def blank?(_),  do: false
end

# The only blank atoms are "false" and "nil"
defimpl Blank, for: Atom do
  def blank?(false), do: true
  def blank?(nil),   do: true
  def blank?(_),     do: false
end

Реализация протокола Blank должна быть определена для всех типов Elixir. Доступные типы:

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

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

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

defimpl Blank, for: MapSet do
  def blank?(enum_like), do: Enum.empty?(enum_like)
end

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

defmodule User do
  defstruct [:email, :name]

  defimpl Blank do
    def blank?(%User{}), do: false
  end
end

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

Обработка случая, когда тип не реализует протокол

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

defprotocol Blank do
  @fallback_to_any true
  def blank?(data)
end

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

defimpl Blank, for: Any do
  def blank?(_), do: true
end

Можно задаться вопросом, почему такое поведение (обработка случая, когда тип не реализует протокол) не является стандартным.

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

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

Типы

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

@spec present?(Blank.t) :: boolean
def present?(blank) do
  not Blank.blank?(blank)
end

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

Рефлексия

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

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

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

Дополнительную информацию о применении консолидации протоколов к данному проекту можно найти в функциях модуля Protocol или задаче mix compile.protocols.

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

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

При создании структуры 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(left, right)

div(integer, integer) :: integer

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

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

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

Примеры

iex> div(5, 2)
2

elem(tuple, index)

elem(tuple, non_neg_integer) :: term

Возвращает элемент с нулевым основанием index в tuple.

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

Примеры

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

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?(atom | tuple, 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(Access.t, [term, ...], (term -> {get, term})) :: {get, Access.t} when get: var

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

Ожидает возврата кортежа, содержащего полученное значение и обновлённое значение.

Использует модуль 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/1 и другие примеры.

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

Возвращает голову списка; возбуждает ArgumentError, если список пуст.

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

Примеры

iex> hd([1, 2, 3, 4])
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 (macro)

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

Примеры

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

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

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

Защитные условия

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

when x in [1, 2, 3]

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

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

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

when x in 1..3

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

when x >= 1 and x <= 3

inspect(arg, opts \\ [])

inspect(Inspect.t, Keyword.t) :: 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) (macro)

Возвращает 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?(atom, atom, integer) :: boolean

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

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

Примеры

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

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

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

Примеры

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 очень полезен при фильтрации или поиске значения в перечислимой коллекции:

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

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

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

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

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

max(first, second)

max(term, term) :: term

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

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

Примеры

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

min(first, second)

min(term, term) :: term

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

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

Примеры

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

node()

node() :: node

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

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

node(arg)

node(pid | reference | port) :: node

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

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

END_OF_DOCUMENT_MARKER

not arg

not boolean :: boolean

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

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(Access.t, [term, ...]) :: {term, Access.t}

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

Использует протокол 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(msg) (макрос)

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

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

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

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

Примеры

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

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

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

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

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

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

Примеры

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

rem(left, right)

rem(integer, integer) :: integer

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

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

Допускается в тестах стражей. Компилятор выполняет встраивание.

Примеры

iex> rem(5, 2)
1

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

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

Работает как raise/1, но не генерирует новый стек вызовов.

Обратите внимание, что System.stacktrace/0 возвращает стек вызовов последнего исключения. В связи с этим, обычно стековый след присваивают в качестве первого выражения в блоке rescue, поскольку любые другие потенциально вызванные (и перехваченные) исключения между блоком обработки исключений и вызовом 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, attrs, 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(number) :: integer

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

Допускается в тестах стражей. Компилятор выполняет встраивание.

Примеры

iex> round(5.6)
6
iex> round(5.2)
5
iex> round(-9.9)
-10

self()

self() :: pid

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

Допускается в блоках стражей. Компилятор выполняет встраивание.

send(dest, msg)

send(dest :: pid | port | atom | {atom, node}, msg) :: msg when msg: 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) (macro)

Обрабатывает сигил ~W.

Возвращает список «слов», разделенных пробелами без экранирования и интерпретации интерполяций.

Модификаторы

  • s: слова в списке являются строками (по умолчанию)
  • a: слова в списке являются атомами
  • c: слова в списке являются списками символов

Примеры

iex> ~W(foo #{bar} baz)
["foo", "\#{bar}", "baz"]

sigil_c(term, modifiers) (macro)

Обрабатывает сигил ~c.

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

Примеры

iex> ~c(foo)
'foo'

iex> ~c(f#{:o}o)
'foo'

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

sigil_r(term, modifiers) (macro)

Обрабатывает сигил ~r.

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

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

Примеры

iex> Regex.match?(~r(foo), "foo")
true

iex> Regex.match?(~r/abc/, "abc")
true

sigil_s(term, modifiers) (macro)

Обрабатывает сигил ~s.

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

Примеры

iex> ~s(foo)
"foo"

iex> ~s(f#{:o}o)
"foo"

iex> ~s(f\#{:o}o)
"f\#{:o}o"

sigil_w(term, modifiers) (macro)

Обрабатывает сигил ~w.

Возвращает список «слов», разделенных пробелами. Для каждого слова происходит экранирование символов и интерполяция.

Модификаторы

  • s: слова в списке являются строками (по умолчанию)
  • a: слова в списке являются атомами
  • c: слова в списке являются списками символов

Примеры

iex> ~w(foo #{:bar} baz)
["foo", "bar", "baz"]

iex> ~w(foo #{" bar baz "})
["foo", "bar", "baz"]

iex> ~w(--source test/enum_test.exs)
["--source", "test/enum_test.exs"]

iex> ~w(foo bar baz)a
[:foo, :bar, :baz]

spawn(fun)

spawn((() -> any)) :: pid

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

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

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

Примеры

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

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

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

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

Примеры

spawn(SomeModule, :function, [1, 2, 3])

spawn_link(fun)

spawn_link((() -> any)) :: pid

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

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

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

Примеры

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

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

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

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

Примеры

spawn_link(SomeModule, :function, [1, 2, 3])

spawn_monitor(fun)

spawn_monitor((() -> any)) :: {pid, reference}

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

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

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

Примеры

current = self()
spawn_monitor(fn -> send current, {self(), 1 + 2} end)

spawn_monitor(module, fun, args)

spawn_monitor(module, atom, list) :: {pid, reference}

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

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

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

Примеры

spawn_monitor(SomeModule, :function, [1, 2, 3])

struct(struct, kv \\ [])

struct(module | map, Enum.t) :: map

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

Аргумент struct может быть атомом (который определяет defstruct) или собственной структурой. Второй аргумент — любой перечисляемый объект, который генерирует кортежи из двух элементов (пары ключ-значение) во время перечисления.

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

Эта функция полезна для динамического создания и обновления структур, а также для преобразования карт в структуры; в последнем случае, просто вставка соответствующего поля :__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, kv \\ [])

struct!(module | map, Enum.t) :: map | 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(maybe_improper_list) :: maybe_improper_list

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

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

Примеры

iex> tl([1, 2, 3, :go])
[2, 3, :go]

to_charlist(arg) (macro)

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

Примеры

iex> to_charlist(:foo)
'foo'

to_string(arg) (macro)

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

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

Примеры

iex> to_string(:foo)
"foo"

trunc(number)

trunc(number) :: integer

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

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

Примеры

iex> trunc(5.4)
5
iex> trunc(5.99)
5

tuple_size(tuple)

tuple_size(tuple) :: non_neg_integer

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

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

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

Примеры

iex> tuple_size {:a, :b, :c}
3

unless(condition, clauses) (macro)

Предоставляет макрос 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) (macro)

Обновляет вложенную структуру с помощью заданной функции 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 \\ []) (макрос)

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

Примеры

Например, для написания тестов с использованием фреймворка ExUnit разработчик должен использовать модуль ExUnit.Case:

defmodule AssertionTest do
  use ExUnit.Case, async: true

  test "always pass" do
    assert true
  end
end

Вызывая use/2, вызовется хук __using__/1 в ExUnit.Case, который выполнит соответствующую настройку.

Проще говоря, use/2 переводится в:

defmodule AssertionTest do
  require ExUnit.Case
  ExUnit.Case.__using__([async: true])

  test "always pass" do
    assert true
  end
end

Где __using__/1 — обычный макрос, который может быть определён в любом модуле:

defmodule MyModule do
  defmacro __using__(opts) do
    quote do
      # code that will run in the module that uses MyModule
    end
  end
end

Рекомендации по применению

__using__/1 обычно используется, когда необходимо установить некоторое состояние (через атрибуты модуля) или обратные вызовы (например, @before_compile) в вызывающую сторону.

__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. Если вы хотите предоставить некоторую существующую функциональность модулю пользователя, определите её в модуле, который будет импортирован соответствующим образом.

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.3.4/Kernel.html

Spec-Zone.ru

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