Spec-Zone.ru › Elixir 1.4

Ядро

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

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

import Kernel, except: [if: 2]

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

Некоторые функции, описанные в этом модуле, встроены компилятором Elixir в свои эквиваленты в модуле :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

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

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, do_block)

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

defoverridable(keywords)

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

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

END_OF_DOCUMENT_MARKER
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(dividend, divisor)

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

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)

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

tl(list)

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

to_charlist(arg)

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

to_string(arg)

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

trunc(number)

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

END_OF_DOCUMENT_MARKER
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.

Не разрешено в условиях (guard clauses).

Примеры

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

iex> !List.first([])
true

left != right

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

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

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

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

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

Примеры

iex> 1 != 2
true

iex> 1 != 1.0
false

left !== right

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

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

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

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

Примеры

iex> 1 !== 2
true

iex> 1 !== 1.0
true

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

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

Не разрешено в условиях (guard clauses).

Примеры

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

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

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

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

Примеры

iex> 1 * 2
2

+value

+value :: value when value: number()

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

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

Примеры

iex> +1
1

left + right

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

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

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

Примеры

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

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

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

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

Примеры

iex> -2
-2

left - right

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

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

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

Примеры

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

Возникает исключение ArithmeticError, если right равно 0 или 0.0.

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

Примеры

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 могут быть сравнены друг с другом.

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

Примеры

iex> 1 < 2
true

left <= right

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

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

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

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

Примеры

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 могут быть сравнены друг с другом.

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

Примеры

iex> 1 == 2
false

iex> 1 == 1.0
true

left === right

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

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

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

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

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

Примеры

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 могут быть сравнены друг с другом.

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

Примеры

iex> 1 > 2
false

left >= right

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

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

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

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

Примеры

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(число)

abs(number()) :: number()

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

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

Примеры

iex> abs(-3.33)
3.33

iex> abs(-3)
3

alias!(псевдоним) (макрос)

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

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

левый и правый (макрос)

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

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

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

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

Примеры

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

apply(функция, аргументы)

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

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

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

Примеры

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

apply(модуль, функция, аргументы)

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

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

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

Примеры

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

binary_part(бинарный, начало, длина)

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

bit_size(bitstring()) :: non_neg_integer()

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

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

Примеры

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

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

byte_size(битовый массив)

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

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

Имена функций и переменных имеют следующий синтаксис: строчная буква 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 как 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(функции, параметры) (макрос)

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

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

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

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

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

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

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

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

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

defmodule MyAppError do
  defexception [:message]
end

value = [:hello]

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

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

defmodule MyAppError do
  defexception [:message]

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

raise MyAppError, value

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

defimpl(имя, параметры, блок_do \\ []) (макрос)

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

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

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

defmacro(вызов, выражение \\ 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(вызов, выражение \\ nil) (макрос)

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

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

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

defmodule(псевдоним, блок_do) (макрос)

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

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

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

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

Типы

Определение протокола автоматически определяет тип, названный 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

  • 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. Тем не менее, служебная информация может быть удалена после консолидации.

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

defstruct(поля) (макрос)

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

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

Для определения структуры разработчик должен определить как функции __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(dividend, divisor)

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

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

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

Допускается в проверках гвардов. Встраивается компилятором.

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

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

Примеры

div(5, 2)
#=> 2

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

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

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

elem(tuple, index)

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

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

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

Допускается в проверках гвардов. Встраивается компилятором.

Примеры

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

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

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

exit(reason)

exit(term()) :: no_return()

Останавливает выполнение вызывающего процесса с заданным сообщением.

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

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

Примеры

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

exit(:normal)

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

exit(:seems_bad)

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

OTP завершения

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

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

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

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

Выходы CLI

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

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

exit({:shutdown, integer})

Это приведет к тому, что процесс ОС завершится со статусом, указанным в integer, одновременно сигнализируя всем связанным процессам 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 вернет значение в соответствии с модулем Access:

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

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

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

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

hd(list)

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

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

Допускается в тестах guard. Встраивается компилятором.

Примеры

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

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

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)

Ограничения

Оператор 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.

Допускается в тестах guard. Встраивается компилятором.

is_binary(term)

is_binary(term()) :: boolean()

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

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

Допускается в тестах guard. Встраивается компилятором.

Примеры

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

is_bitstring(term)

is_bitstring(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

Примеры

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

is_boolean(term)

is_boolean(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

is_float(term)

is_float(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

is_function(term)

is_function(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

is_function(term, arity)

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

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

Допускается в тестах guard. Встраивается компилятором.

Примеры

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.

Допускается в тестах guard. Встраивается компилятором.

is_list(term)

is_list(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

is_map(term)

is_map(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

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.

Допускается в тестах guard. Встраивается компилятором.

is_pid(term)

is_pid(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

is_port(term)

is_port(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

is_reference(term)

is_reference(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

is_tuple(term)

is_tuple(term()) :: boolean()

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

Допускается в тестах guard. Встраивается компилятором.

length(list)

length(list()) :: non_neg_integer()

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

Допускается в тестах guard. Встраивается компилятором.

Примеры

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

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

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

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

Допускается в тестах guard. Встраивается компилятором.

Примеры

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

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(first, second) ::
  first |
  second when first: term(), second: term()

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

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

Примеры

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

min(first, second)

min(first, second) ::
  first |
  second when first: term(), second: 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.

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

not arg

not true :: false
not false :: true

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

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.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(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(float()) :: integer()
round(value) :: value when value: 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, 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) (макрос)

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

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

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

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

Примеры

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

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

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

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

Примеры

iex> ~c(foo)
'foo'

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

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

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

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

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

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

Примеры

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

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

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

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

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

Примеры

iex> ~s(foo)
"foo"

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

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

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

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

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

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

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

Примеры

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

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

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

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

spawn(fun)

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

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

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

Анонимная функция получает 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()

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

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

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

Примеры

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

spawn_link(fun)

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

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

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

Анонимная функция получает 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()

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

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

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

Примеры

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

spawn_monitor(fun)

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

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

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

Анонимная функция получает 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 и ссылку на отслеживание.

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

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

Примеры

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

struct(struct, kv \\ [])

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, kv \\ [])

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

to_charlist(arg) (макрос)

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

Примеры

iex> to_charlist(:foo)
'foo'

to_string(arg) (макрос)

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

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

Примеры

iex> to_string(:foo)
"foo"

trunc(number)

trunc(value) :: value when value: integer()
trunc(float()) :: 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.4.5/Kernel.html

Spec-Zone.ru

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