Spec-Zone.ru › Elixir 1.8

Ядро

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

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

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

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

iex> is_number(13)
true

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

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

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

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

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

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

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

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

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

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

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

Типы данных

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

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

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

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

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

Протоколы

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

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

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

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

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

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

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

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

Стражи

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

def drive(%User{age: age}) when age >= 16 do
  ...
end

Вышеприведенная строка будет вызвана только в том случае, если возраст пользователя не менее 16 лет. Более полное введение в стражи доступно на странице Стражи.

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

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

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

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

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

Резюме

Стражи

left != right

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

left !== right

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

left * right

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

+value

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

left + right

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

-value

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

left - right

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

left / right

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

left < right

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

left <= right

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

left == right

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

left === right

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

left > right

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

left >= right

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

abs(number)

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

left and right

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

binary_part(binary, start, length)

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

bit_size(bitstring)

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

byte_size(bitstring)

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

ceil(number)

Возвращает наименьшее целое число, не большее чем number.

div(dividend, divisor)

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

elem(tuple, index)

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

floor(number)

Возвращает наибольшее целое число, не большее чем number.

hd(list)

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

left in right

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

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.

map_size(map)

Возвращает размер отображения.

node()

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

node(arg)

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

not(value)

Логическое НЕ.

left or right

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

rem(dividend, divisor)

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

round(number)

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

self()

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

tl(list)

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

trunc(number)

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

tuple_size(tuple)

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

Функции

!value

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

left && right

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

left ++ right

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

left -- right

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

first..last

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

left <> right

Конкатенирует два двоичных числа.

left =~ right

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

@expr

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

alias!(alias)

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

apply(fun, args)

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

apply(module, function_name, args)

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

binding(context \\ nil)

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

def(call, expr \\ nil)

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

defdelegate(funs, opts)

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

defexception(fields)

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

defguard(guard)

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

defguardp(guard)

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

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

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

defmacro(call, expr \\ nil)

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

defmacrop(call, expr \\ nil)

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

defmodule(alias, do_block)

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

defoverridable(keywords_or_behaviour)

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

defp(call, expr \\ nil)

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

defprotocol(name, do_block)

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

defstruct(fields)

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

destructure(left, right)

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

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)

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

if(condition, clauses)

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

inspect(term, opts \\ [])

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

macro_exported?(module, macro, arity)

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

make_ref()

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

match?(pattern, expr)

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

max(first, second)

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

min(first, second)

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

pop_in(path)

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

pop_in(data, keys)

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

put_elem(tuple, index, value)

Помещает value в заданную позицию (нумерация с нуля) в tuple.

put_in(path, value)

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

put_in(data, keys, value)

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

raise(message)

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

raise(exception, attributes)

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

reraise(message, stacktrace)

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

reraise(exception, attributes, stacktrace)

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

send(dest, message)

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

sigil_C(term, modifiers)

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

sigil_D(date_string, modifiers)

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

sigil_N(naive_datetime_string, modifiers)

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

sigil_R(term, modifiers)

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

sigil_S(term, modifiers)

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

sigil_T(time_string, 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)
END_OF_DOCUMENT_MARKER

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

spawn(module, fun, args)

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

spawn_link(fun)

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

spawn_link(module, fun, args)

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

spawn_monitor(fun)

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

spawn_monitor(module, fun, args)

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

struct(struct, fields \\ [])

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

struct!(struct, fields \\ [])

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

throw(term)

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

to_charlist(term)

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

to_string(term)

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

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

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

Ограничения

left != right

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

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

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

Все термины в 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

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

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

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

Примеры

iex> 1 * 2
2

+value

+value :: value when value: number()

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

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

Примеры

iex> +1
1

left + right

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

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

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

Примеры

iex> 1 + 2
3

-value

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

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

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

Примеры

iex> -2
-2

left - right

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

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

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

Примеры

iex> 1 - 2
-1

left / right

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

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

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

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

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

Примеры

1 / 2
#=> 0.5

-3.0 / 2.0
#=> -1.5

5 / 1
#=> 5.0

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

left < right

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

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

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

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

Примеры

iex> 1 < 2
true

left <= right

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

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

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

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

Примеры

iex> 1 <= 2
true

left == right

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

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

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

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

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

Примеры

iex> 1 == 2
false

iex> 1 == 1.0
true

left === right

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

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

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

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

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

Примеры

iex> 1 === 2
false

iex> 1 === 1.0
false

left > right

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

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

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

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

Примеры

iex> 1 > 2
false

left >= right

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

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

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

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

Примеры

iex> 1 >= 2
false

abs(number)

abs(number()) :: number()

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

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

Примеры

iex> abs(-3.33)
3.33

iex> abs(-3)
3

left and right

(макрос)

Булево И.

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

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

Разрешено в тестах ограничений.

Примеры

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

binary_part(binary, start, length)

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

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

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

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

Примеры

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

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

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

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

ceil(number)

(с 1.8.0)
ceil(number()) :: integer()

Возвращает наименьшее целое число, не большее number.

Если нужно выполнить операцию ceil для других десятичных знаков, используйте Float.ceil/2 вместо этого.

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

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

floor(number)

(с 1.8.0)
floor(number()) :: integer()

Возвращает наибольшее целое число, не большее number.

Если нужно выполнить операцию floor для других десятичных знаков, используйте Float.floor/2 вместо этого.

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

hd(list)

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

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

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

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

Примеры

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

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

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

left in right

(макрос)

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

Примеры

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

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

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

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

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

Стражи

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

when x in [1, 2, 3]

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

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

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

when x in 1..3

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

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

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

Рассмотрение AST

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

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

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

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

is_atom(term)

is_atom(term()) :: boolean()

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

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

is_binary(term)

is_binary(term()) :: boolean()

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

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

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

Примеры

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

is_bitstring(term)

is_bitstring(term()) :: boolean()

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

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

Примеры

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

is_boolean(term)

is_boolean(term()) :: boolean()

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

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

is_float(term)

is_float(term()) :: boolean()

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

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

is_function(term)

is_function(term()) :: boolean()

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

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

is_function(term, arity)

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

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

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

Примеры

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

is_integer(term)

is_integer(term()) :: boolean()

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

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

is_list(term)

is_list(term()) :: boolean()

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

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

is_map(term)

is_map(term()) :: boolean()

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

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

is_nil(term)

(макрос)

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

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

Примеры

iex> is_nil(1)
false

iex> is_nil(nil)
true

is_number(term)

is_number(term()) :: boolean()

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

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

is_pid(term)

is_pid(term()) :: boolean()

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

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

is_port(term)

is_port(term()) :: boolean()

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

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

is_reference(term)

is_reference(term()) :: boolean()

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

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

is_tuple(term)

is_tuple(term()) :: boolean()

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

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

length(list)

length(list()) :: non_neg_integer()

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

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

Примеры

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

map_size(map)

map_size(map()) :: non_neg_integer()

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

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

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

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

Примеры

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

node()

node() :: node()

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

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

node(arg)

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

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

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

not(value)

not true :: false
not false :: true

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

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

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

Примеры

iex> not false
true

left or right

(макрос)

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

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

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

Разрешено в тестах-стражах.

Примеры

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

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

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 (идентификатор процесса) вызывающего процесса.

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

tl(list)

tl(nonempty_maybe_improper_list(elem, tail)) ::
  maybe_improper_list(elem, tail) | tail
when elem: term(), tail: term()

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

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

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

Примеры

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

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

tl([:one])
#=> []

tl([:a, :b | :c])
#=> [:b | :c]

tl([:a | %{b: 1}])
#=> %{b: 1}

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

Функции

!value

(макрос)

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

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

Не разрешено в условиях стража.

Примеры

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

iex> !List.first([])
true

left && right

(макрос)

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

Не разрешено в условиях стража.

Примеры

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

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]

left -- right

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

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

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

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

Примеры

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

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

first..last

(макрос)

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

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

Примеры

iex> 0 in 1..3
false

iex> 1 in 1..3
true

iex> 2 in 1..3
true

iex> 3 in 1..3
true

left <> right

(макрос)

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

Примеры

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

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

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

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

left =~ right

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

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

Возвращает 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

@expr

(макрос)

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

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

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

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

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

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

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

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

MyServer.first_data()
#=> 11

MyServer.second_data()
#=> 13

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

alias!(alias)

(макрос)

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

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

apply(fun, args)

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

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

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

Примеры

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

apply(module, function_name, args)

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

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

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

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

Примеры

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

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]

def(call, expr \\ nil)

(макрос)

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

END_OF_DOCUMENT_MARKER

Примеры

defmodule Foo do
  def bar, do: :baz
end

Foo.bar()
#=> :baz

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

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

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

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

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

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

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

MyMath.multiply_by(4)
#=> 8

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

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

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

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

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

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

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

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

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

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

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

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

rescue/catch/after/else

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

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

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

defdelegate(funs, opts)

(макрос)

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

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

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

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

Параметры

  • :to - модуль, к которому будет происходить переадресация.

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

Примеры

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

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

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

defexception(fields)

(макрос)

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

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

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

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

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

Поднятие исключений

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

defmodule MyAppError do
  defexception [:message]
end

value = [:hello]

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

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

defmodule MyAppError do
  defexception [:message]

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

raise MyAppError, value

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

defguard(guard)

(макрос)(с версии 1.6.0)
defguard(Macro.t()) :: Macro.t()

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

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

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

Пример

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

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

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

  defp step(1, step_count) do
    step_count
  end

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

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

defguardp(guard)

(макрос)(с версии 1.6.0)
defguardp(Macro.t()) :: Macro.t()

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

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

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

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

(макрос)

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

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

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

defmacro(call, expr \\ nil)

(макрос)

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

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

Примеры

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

require MyLogic

MyLogic.unless false do
  IO.puts("It works")
end

defmacrop(call, expr \\ nil)

(макрос)

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

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

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

defmodule(alias, do_block)

(макрос)

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

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

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

Примеры

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

Вложенность

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

defmodule Foo do
  defmodule Bar do
  end
end

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

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

defmodule Foo.Bar do
  # code
end

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

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

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

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

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

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

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

Зарезервированные имена модулей

Если вы попытаетесь определить модуль, который уже существует, вы получите предупреждение о том, что модуль был переопределён.

Существуют некоторые модули, которые Elixir пока не реализует, но которые могут быть реализованы в будущем. Эти модули зарезервированы, и их определение приведёт к ошибке компиляции:

defmodule Any do
  # code
end
#=> ** (CompileError) iex:1: module Any is reserved and cannot be defined

Elixir зарезервировал следующие имена модулей: Elixir, Any, BitString, PID, и Reference.

defoverridable(keywords_or_behaviour)

(макрос)

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

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

Пример

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

      defoverridable test: 2
    end
  end
end

defmodule InheritMod do
  use DefaultMod

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

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

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

Пример

defmodule Behaviour do
  @callback foo :: any
end

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

      def foo do
        "Override me"
      end

      defoverridable Behaviour
    end
  end
end

defmodule InheritMod do
  use DefaultMod

  def foo do
    "Overridden"
  end
end

defp(call, expr \\ nil)

(макрос)

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

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

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

Примеры

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

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

Foo.bar()
#=> 3

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

defprotocol(name, do_block)

(макрос)

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

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

Примеры

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

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

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

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

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

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

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

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

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

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

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

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

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

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

defmodule User do
  defstruct [:email, :name]

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

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

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

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

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

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

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

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

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

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

defprotocol Reversible do
  def reverse(term)
end

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

Типы

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

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

  IO.puts(result)
end

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

Рефлексия

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

  • __protocol__/1 — возвращает информацию о протоколе. Функция принимает одно из следующих атомов:

    • :consolidated? — возвращает, является ли протокол консолидированным

    • :functions — возвращает список ключевых слов функций протокола и их арностей

    • :impls — если консолидирован, возвращает {:consolidated, modules} со списком модулей, реализующих протокол, в противном случае :not_consolidated

    • :module — имя атома модуля протокола

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

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

Например, для протокола Enumerable у нас есть:

iex> Enumerable.__protocol__(:functions)
[count: 1, member?: 2, reduce: 3, slice: 1]

iex> Enumerable.impl_for([])
Enumerable.List

iex> Enumerable.impl_for(42)
nil

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

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

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

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

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

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

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

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

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

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

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

defstruct(fields)

(макрос)

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

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

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

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

Примеры

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

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

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

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

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

Вывод

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

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

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

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

MyProtocol.call(john) #=> works

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

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

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

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

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

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

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

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

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

Типы

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

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

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

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

Структуры, внутренняя структура которых является приватной для локального модуля (сопоставление с образцом или прямой доступ к полям не должны быть разрешены), должны использовать атрибут @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 (как и оператор =).

exit(reason)

exit(term()) :: no_return()

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

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

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

Примеры

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

exit(:normal)

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

exit(:seems_bad)

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

Выходы OTP

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

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

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

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

Выходы CLI

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

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

exit({:shutdown, integer})

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

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

function_exported?(module, function, arity)

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

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

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

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

Примеры

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

get_and_update_in(path, fun)

(макрос)

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

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

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

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

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

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

Примеры

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

Пути

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

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

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

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

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

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

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

# Does not access any key or field
users

get_and_update_in(data, keys, fun)

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

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

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

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

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

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

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

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

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

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

Примеры

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

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

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

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

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

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

get_in(data, keys)

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

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

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

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

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

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

Примеры

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

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

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

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

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

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

if(condition, clauses)

(макрос)

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

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

Примеры с одной строкой

if(foo, do: bar)

В примере выше, bar будет возвращено, если foo вычисляется как истинное значение (ни 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.

inspect(term, opts \\ [])

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

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

Параметры

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

Примеры

iex> inspect(:foo)
":foo"

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

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

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

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

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

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

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

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

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

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

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

Протокол Inspect может быть изменён для скрытия определённых полей структур, чтобы они не отображались в логах, результатах проверки и т.п. Подробную информацию см. в разделе «Производные» документации к протоколу Inspect.

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

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

Возвращённая ссылка может повториться после примерно 282 вызовов; поэтому она достаточно уникальна для практического применения.

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

Примеры

make_ref()
#=> #Reference<0.0.0.135>

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

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

Также можно задавать условия к соответствию:

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

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

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

max(first, second)

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

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

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

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

Примеры

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

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

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

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

min(first, second)

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

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

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

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

Примеры

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

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

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

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

pop_in(path)

(macro)

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

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

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

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

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

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

Примеры

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

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

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

pop_in(data, keys)

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

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

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

Примеры

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

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

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

put_elem(tuple, index, value)

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

Заменяет value в заданном index (с нумерацией с нуля) в tuple.

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

Примеры

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

put_in(path, value)

(macro)

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

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

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

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

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

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

Примеры

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

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

put_in(data, keys, value)

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

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

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

Примеры

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

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

raise(message)

(macro)

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

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

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

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

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

Примеры

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

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

raise(exception, attributes)

(macro)

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

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

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

Примеры

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

reraise(message, stacktrace)

(macro)

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

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

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

Примеры

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

reraise(exception, attributes, stacktrace)

(macro)

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

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

Примеры

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

send(dest, message)

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

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

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

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

Примеры

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

sigil_C(term, modifiers)

(macro)

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

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

Примеры

iex> ~C(foo)
'foo'

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

sigil_D(date_string, modifiers)

(macro)

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

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

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

Примеры

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

sigil_N(naive_datetime_string, modifiers)

(macro)

Обрабатывает сигил ~N для неявных дат и времён.

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

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

Примеры

iex> ~N[2015-01-13 13:00:07]
~N[2015-01-13 13:00:07]
iex> ~N[2015-01-13T13:00:07.001]
~N[2015-01-13 13:00:07.001]

sigil_R(term, modifiers)

(macro)

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

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

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

Примеры

iex> Regex.match?(~R(f#{1,3}o), "f#o")
true

sigil_S(term, modifiers)

(macro)

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

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

Примеры

iex> ~S(foo)
"foo"

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

sigil_T(time_string, modifiers)

(macro)

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

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

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

Примеры

iex> ~T[13:00:07]
~T[13:00:07]
iex> ~T[13:00:07.001]
~T[13:00:07.001]

sigil_W(term, modifiers)

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

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

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

Дополнительные функции, связанные с процессами, можно найти в модуле Process.

Анонимная функция получает 0 аргументов и может возвращать любое значение.

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

Примеры

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

receive do
  {^child, 3} -> IO.puts("Received 3 back")
end

spawn(module, fun, args)

spawn(module(), atom(), list()) :: pid()

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

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

Дополнительные функции, связанные с процессами, можно найти в модуле Process.

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

Примеры

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

spawn_link(fun)

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

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

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

Дополнительные функции, связанные с процессами, можно найти в модуле Process. Дополнительную информацию о связывании можно найти в Process.link/1.

Анонимная функция получает 0 аргументов и может возвращать любое значение.

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

Примеры

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

receive do
  {^child, 3} -> IO.puts("Received 3 back")
end

spawn_link(module, fun, args)

spawn_link(module(), atom(), list()) :: pid()

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

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

Дополнительные функции, связанные с процессами, можно найти в модуле Process. Дополнительную информацию о связывании можно найти в Process.link/1.

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

Примеры

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

spawn_monitor(fun)

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

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

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

Дополнительные функции, связанные с процессами, можно найти в модуле Process.

Анонимная функция получает 0 аргументов и может возвращать любое значение.

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

Примеры

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

spawn_monitor(module, fun, args)

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

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

Обычно разработчики не используют функции spawn, вместо этого они используют абстракции, такие как Task, GenServer и Agent, построенные поверх spawn, которые запускают процессы с большей удобством с точки зрения интроспекции и отладки.

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

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

Примеры

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

struct(struct, fields \\ [])

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

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

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

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

Эта функция полезна для динамического создания и обновления структур, а также для преобразования карт в структуры; в последнем случае, просто вставка соответствующего :__struct__ поля в карту может быть недостаточно, и вместо этого следует использовать struct/2.

Примеры

defmodule User do
  defstruct name: "john"
end

struct(User)
#=> %User{name: "john"}

opts = [name: "meg"]
user = struct(User, opts)
#=> %User{name: "meg"}

struct(user, unknown: "value")
#=> %User{name: "meg"}

struct(User, %{name: "meg"})
#=> %User{name: "meg"}

# String keys are ignored
struct(User, %{"name" => "meg"})
#=> %User{name: "john"}

struct!(struct, fields \\ [])

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

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

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

to_charlist(term)

(макрос)

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

Примеры

iex> to_charlist(:foo)
'foo'

to_string(term)

(макрос)

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

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

Примеры

iex> to_string(:foo)
"foo"

unless(condition, clauses)

(макрос)

Предоставляет макрос unless.

Этот макрос оценивает и возвращает блок do, переданный как второй аргумент, если clause оценивается как ложное значение (ни false, ни nil). В противном случае он возвращает значение блока 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

(макрос)

Обеспечивает оператор короткого замыкания, который вычисляет и возвращает второе выражение только в том случае, если первое выражение не оценивается как истинное значение (то есть, оно либо 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.8.2/Kernel.html

Spec-Zone.ru

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