Spec-Zone.ru › Elixir 1.18

Исходный код Ядро

Kernel — это стандартная среда выполнения Elixir.

Она в основном состоит из:

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

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

iex> is_number(13)
true

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

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

Дополнительную информацию об импорте см. в import/2.

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

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

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

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

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

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

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

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

Типы данных

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

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

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

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

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

Протоколы

Протоколы добавляют полиморфную отправку в Elixir. Они являются контрактами, реализуемыми типами данных. Дополнительную информацию о протоколах см. в Protocol. 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 и их приоритетов
  • Шаблоны и условия — введение в шаблоны, условия и расширения
  • Справочник по синтаксису — справочник по синтаксису языка
  • Справочник по типам — спецификации типов и функций, включая список типов
  • Синтаксис Unicode — обзор поддержки Unicode в Elixir

Условные операторы

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

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

Приведенная выше строка выполнится только в том случае, если возрасту пользователя 16 лет или больше. Условия также поддерживают объединение нескольких условий с помощью and и or. Всё условие истинно, если все выражения условия будут истинными true. Более полное введение в условия можно найти на странице Шаблоны и условия.

Истинностные и ложные значения

Помимо булевых значений true и false, в Elixir существует понятие «истинностного» или «ложного» значения.

  • значение истинно, когда оно не false и не nil
  • значение ложно, когда оно либо false, либо nil

Elixir имеет функции, такие как and/2, которые работают только с булевыми значениями, но также функции, которые работают с этими истинностными/ложными значениями, такие как &&/2 и !/1.

Структурное сравнение

Функции в этом модуле выполняют структурное сравнение. Это позволяет сравнивать разные типы данных с помощью операторов сравнения:

1 < :an_atom

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

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

Один пример, показывающий различия между структурным и семантическим сравнением, — это строки: «alien» сортируется меньше, чем «office» ("alien" < "office") но «álien» больше, чем «office». Это происходит потому, что < сравнивает основанные байты, которые образуют строку. Если вы делаете алфавитный список, то вы можете захотеть, чтобы «álien» также появлялся перед «office».

Это означает, что сравнения в Elixir структурные, так как они стремятся сравнивать типы данных максимально эффективно, чтобы создавать гибкие и производительные структуры данных. Это различие особенно важно для функций, которые обеспечивают упорядочение, такие как >/2, </2, >=/2, <=/2, min/2 и max/2. Например:

~D[2017-03-31] > ~D[2017-04-01]

вернёт true , потому что структурное сравнение сравнивает поле :day перед :month или :year. К счастью, компилятор Elixir обнаружит, когда сравниваются структуры или когда сравнивается код, который всегда истинен или ложен, и выведет соответствующее предупреждение.

Для выполнения семантических сравнений соответствующие типы данных предоставляют функцию compare/2 , такую как Date.compare/2:

iex> Date.compare(~D[2017-03-31], ~D[2017-04-01])
:lt

В качестве альтернативы можно использовать функции в модуле Enum для сортировки или вычисления максимума/минимума:

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

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

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

Порядок терминов

number < atom < reference < function < port < pid < tuple < map < list < bitstring

При сравнении двух чисел разных типов (число — это либо целое число, либо число с плавающей точкой), всегда происходит преобразование в тип с большей точностью, если оператор сравнения не является ===/2 или !==/2. Число с плавающей точкой считается более точным, чем целое число, если число с плавающей точкой не больше/меньше +/-9007199254740992.0 соответственно, в противном случае все значащие цифры числа с плавающей точкой находятся слева от десятичной точки. Это поведение существует для того, чтобы сравнение больших чисел оставалось транзитивным.

Типы коллекций сравниваются по следующим правилам:

  • Кортежи сравниваются по размеру, а затем по элементам.
  • Карты сравниваются по размеру, а затем по парам ключ-значение.
  • Списки сравниваются по элементам.
  • Битовые строки сравниваются по байтам, неполные байты сравниваются по битам.
  • Атомы сравниваются по их строковому значению, по кодовым точкам.

Примеры

Мы можем проверить истинность значения, дважды используя функцию !/1.

Истинностные значения:

iex> !!true
true
iex> !!5
true
iex> !![1,2]
true
iex> !!"foo"
true

Ложные значения (их ровно два):

iex> !!false
false
iex> !!nil
false

Встраивание

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

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

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

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

Резюме

Защиты

left * right

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

+value

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

left + right

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

-value

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

left - right

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

left / right

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

left != right

Оператор неравенства.

left !== right

Оператор строгого неравенства.

left < right

Оператор меньше.

left <= right

Оператор меньше или равно.

left == right

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

left === right

Оператор строгого равенства.

left > right

Оператор больше.

left >= right

Оператор больше или равно.

abs(number)

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

left and right

Оператор строгой булевой "и".

binary_part(binary, start, size)

Извлекает часть бинарного по start с size.

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

Возвращает true если term это исключение, иначе возвращает false.

is_exception(term, name)

Возвращает true если term это исключение типа name, иначе возвращает 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_map_key(map, key)

Возвращает true если key это ключ в map, иначе возвращает false.

is_nil(term)

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

is_non_struct_map(term)

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

is_number(term)

Возвращает true если term это целое или вещественное число, иначе возвращает false.

is_pid(term)

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

is_port(term)

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

END_OF_DOCUMENT_MARKER
is_reference(term)

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

is_struct(term)

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

is_struct(term, name)

Возвращает true если term является структурой name, в противном случае возвращает 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)

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

Функции

left && right

Оператор логического "и".

base ** exponent

Оператор возведения в степень.

left ++ right

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

left -- right

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

..

Создаёт диапазон с полным слайсом 0..-1//1.

first..last

Создаёт диапазон от first до last.

first..last//step

Создаёт диапазон от first до last с шагом step.

!value

Оператор логического "не".

left <> right

Бинарный оператор конкатенации. Конкатенирует два бинарных значения.

left =~ right

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

@expr

Унарный оператор атрибута модуля.

alias!(alias)

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

apply(fun, args)

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

apply(module, function_name, args)

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

binary_slice(binary, range)

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

binary_slice(binary, start, size)

Возвращает бинарное значение, начинающееся со смещения start и имеющее заданный размер size.

binding(context \\ nil)

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

dbg(code \\ quote do binding() end, options \\ [])

Отлаживает данную code.

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

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

get_in(data, keys)

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

if(condition, clauses)

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

inspect(term, opts \\ [])

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

macro_exported?(module, macro, arity)

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

make_ref()

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

match?(pattern, expr)

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

END_OF_DOCUMENT_MARKER
max(first, second)

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

min(first, second)

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

pop_in(path)

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

pop_in(data, keys)

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

put_elem(tuple, index, value)

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

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_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_s(term, modifiers)

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

sigil_T(time_string, modifiers)

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

sigil_U(datetime_string, modifiers)

Обрабатывает сигил ~U для создания UTC DateTime.

sigil_W(term, modifiers)

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

sigil_w(term, modifiers)

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

spawn(fun)

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

spawn(module, fun, args)

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

spawn_link(fun)

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

spawn_link(module, fun, args)

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

spawn_monitor(fun)

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

spawn_monitor(module, fun, args)

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

struct(struct, fields \\ [])

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

struct!(struct, fields \\ [])

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

tap(value, fun)

Передаёт первый аргумент, value, во второй аргумент, функцию fun, и возвращает value само.

then(value, fun)

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

throw(term)

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

to_charlist(term)

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

to_string(term)

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

to_timeout(duration)

Создаёт таймаут в миллисекундах из заданных компонентов.

unless(condition, clauses) deprecated

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

update_in(path, fun)

Обновляет вложенную структуру по заданному path.

update_in(data, keys, fun)

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

use(module, opts \\ [])

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

var!(var, context \\ nil)

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

left |> right

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

left || right

Логический оператор "или".

Операторы

left * rightSource

@spec integer() * integer() :: integer()
@spec float() * float() :: float()
@spec integer() * float() :: float()
@spec float() * integer() :: float()

Оператор умножения.

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

Примеры

iex> 1 * 2
2

+valueSource

@spec +integer() :: integer()
@spec +float() :: float()

Унарный оператор положительного значения.

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

Примеры

iex> +1
1

left + rightSource

@spec integer() + integer() :: integer()
@spec float() + float() :: float()
@spec integer() + float() :: float()
@spec float() + integer() :: float()

Оператор сложения.

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

Примеры

iex> 1 + 2
3

-valueSource

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

Унарный оператор отрицательного значения.

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

Примеры

iex> -2
-2

left - rightSource

@spec integer() - integer() :: integer()
@spec float() - float() :: float()
@spec integer() - float() :: float()
@spec float() - integer() :: float()

Оператор вычитания.

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

Примеры

iex> 1 - 2
-1

left / rightSource

@spec 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 != rightSource

@spec term() != term() :: boolean()

Оператор неравенства.

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

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

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

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

Примеры

iex> 1 != 2
true

iex> 1 != 1.0
false

left !== rightSource

@spec term() !== term() :: boolean()

Оператор строгого неравенства.

Возвращает true , если два значения не точно равны. См. ===/2 для определения того, что считается "точно равным".

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

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

Примеры

iex> 1 !== 2
true

iex> 1 !== 1.0
true

left < rightSource

@spec term() < term() :: boolean()

Оператор меньше.

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

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

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

Примеры

iex> 1 < 2
true

left <= rightSource

@spec term() <= term() :: boolean()

Оператор меньше или равно.

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

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

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

Примеры

iex> 1 <= 2
true

left == rightSource

@spec term() == term() :: boolean()

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

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

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

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

Примеры

iex> 1 == 2
false

iex> 1 == 1.0
true

left === rightSource

@spec term() === term() :: boolean()

Оператор строгого равенства.

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

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

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

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

Примеры

iex> 1 === 2
false

iex> 1 === 1.0
false

left > rightSource

@spec term() > term() :: boolean()

Оператор больше.

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

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

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

Примеры

iex> 1 > 2
false

left >= rightSource

@spec term() >= term() :: boolean()

Оператор больше или равно.

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

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

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

Примеры

iex> 1 >= 2
false

abs(number)Source

@spec abs(number()) :: number()

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

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

Примеры

iex> abs(-3.33)
3.33

iex> abs(-3)
3
END_OF_DOCUMENT_MARKER

слева и справаИсходный код

Оператор "и" строго для булевых значений.

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

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

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

Примеры

iex> true and false
false

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

iex> "yay!" and true
** (BadBooleanError) expected a boolean on left-side of "and", got: "yay!"

binary_part(бинарный, начало, размер)Исходный код

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

Извлекает часть бинарного start с size.

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

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

Примеры

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

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

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

Исключение ArgumentError генерируется, когда size выходит за пределы бинарного объекта:

binary_part("Hello", 0, 10)
** (ArgumentError) argument error

размер_в_битах(битовая_строка)Исходный код

@spec bit_size(bitstring()) :: non_neg_integer()

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

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

Примеры

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

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

размер_в_байтах(битовая_строка)Исходный код

@spec 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(число)Исходный код

@spec ceil(number()) :: integer()

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

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

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

Примеры

iex> ceil(10)
10

iex> ceil(10.1)
11

iex> ceil(-10.1)
-10

div(делимое, делитель)Исходный код

@spec 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(кортеж, индекс)Исходный код

@spec 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(число)Исходный код

@spec floor(number()) :: integer()

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

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

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

Примеры

iex> floor(10)
10

iex> floor(9.7)
9

iex> floor(-9.7)
-10

hd(список)Исходный код

@spec hd(nonempty_maybe_improper_list(elem, term())) :: elem when elem: term()

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

Голова списка — это его первый элемент.

Работает с несоблюдающими списками.

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

Примеры

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

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

При передаче пустого списка генерируется:

hd([])
** (ArgumentError) argument error

слева вправоИсходный код

Оператор принадлежности.

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

Примеры

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

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

Если правая сторона является диапазоном, будет выполнена более эффективная проверка сравнения. Например:

when x in 1..1000

приблизительно переводится в:

when x >= 1 and x <= 1000

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

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

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

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

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

is_atom(термин)Исходный код

@spec is_atom(term()) :: boolean()

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

Обратите внимание, что true, false, и nil являются атомами в Elixir, а также именами модулей. Поэтому эта функция вернёт true для всех этих значений.

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

Примеры

iex> is_atom(:name)
true

iex> is_atom(false)
true

iex> is_atom(AnAtom)
true

iex> is_atom("string")
false

is_binary(термин)Исходный код

@spec is_binary(term()) :: boolean()

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

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

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

Примеры

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

is_bitstring(термин)Исходный код

@spec is_bitstring(term()) :: boolean()

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

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

Примеры

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

is_boolean(term)Source

@spec is_boolean(term()) :: boolean()

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

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

Примеры

iex> is_boolean(false)
true

iex> is_boolean(true)
true

iex> is_boolean(:test)
false

is_exception(term)Source

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

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

Примеры

iex> is_exception(%RuntimeError{})
true

iex> is_exception(%{})
false

is_exception(term, name)Source

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

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

Примеры

iex> is_exception(%RuntimeError{}, RuntimeError)
true

iex> is_exception(%RuntimeError{}, Macro.Env)
false

is_float(term)Source

@spec is_float(term()) :: boolean()

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

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

is_function(term)Source

@spec is_function(term()) :: boolean()

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

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

Примеры

iex> is_function(fn x -> x + x end)
true

iex> is_function("not a function")
false

is_function(term, arity)Source

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

@spec is_integer(term()) :: boolean()

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

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

is_list(term)Source

@spec is_list(term()) :: boolean()

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

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

is_map(term)Source

@spec is_map(term()) :: boolean()

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

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

Структуры — это карты

Структуры также являются картами, и многие структуры данных Elixir реализованы с помощью структур: Rangeы, Regexы, Dateы…

iex> is_map(1..10)
true
iex> is_map(~D[2024-04-18])
true

Если вы хотите проверить только карты, которые не являются структурами, используйте is_non_struct_map/1 вместо этого.

iex> is_non_struct_map(1..10)
false

is_map_key(map, key)Source

@spec is_map_key(map(), term()) :: boolean()

Возвращает true если key это ключ в map, в противном случае возвращает false.

Вызывает BadMapError, если первый элемент не является картой.

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

Примеры

iex> is_map_key(%{a: "foo", b: "bar"}, :a)
true

iex> is_map_key(%{a: "foo", b: "bar"}, :c)
false

is_nil(term)Source

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

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

Примеры

iex> is_nil(1 + 2)
false

iex> is_nil(nil)
true

is_non_struct_map(term)Source

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

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

Примеры

iex> is_non_struct_map(%{})
true

iex> is_non_struct_map(URI.parse("/"))
false

iex> is_non_struct_map(nil)
false

is_number(term)Source

@spec is_number(term()) :: boolean()

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

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

is_pid(term)Source

@spec is_pid(term()) :: boolean()

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

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

is_port(term)Source

@spec is_port(term()) :: boolean()

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

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

is_reference(term)Source

@spec is_reference(term()) :: boolean()

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

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

is_struct(term)Source

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

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

Примеры

iex> is_struct(URI.parse("/"))
true

iex> is_struct(%{})
false

is_struct(term, name)Source

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

is_struct/2 не проверяет, существует ли name и является ли она допустимой структурой. Если вам нужны такие проверки, вы должны использовать сопоставление с образцом для структуры, например, match?(%URI{}, arg).

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

Примеры

iex> is_struct(URI.parse("/"), URI)
true

iex> is_struct(URI.parse("/"), Macro.Env)
false

is_tuple(term)Source

@spec is_tuple(term()) :: boolean()

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

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

END_OF_DOCUMENT_MARKER

length(list)Source

@spec length(list()) :: non_neg_integer()

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

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

Примеры

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

map_size(map)Source

@spec map_size(map()) :: non_neg_integer()

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

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

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

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

Примеры

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

node()Source

@spec node() :: node()

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

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

node(arg)Source

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

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

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

not valueSource

@spec not true :: false
@spec not false :: true

Оператор строгой логической "не".

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

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

Примеры

iex> not false
true

left or rightSource

Оператор строгой логической "или".

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

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

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

Примеры

iex> true or false
true

iex> false or 42
42

iex> 42 or false
** (BadBooleanError) expected a boolean on left-side of "or", got: 42

rem(dividend, divisor)Source

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

@spec round(number()) :: integer()

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

Если число равноудалено от двух ближайших целых чисел, округляется в сторону от нуля.

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

Примеры

iex> round(5.6)
6

iex> round(5.2)
5

iex> round(-9.9)
-10

iex> round(-9)
-9

iex> round(2.5)
3

iex> round(-2.5)
-3

self()Source

@spec self() :: pid()

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

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

tl(list)Source

@spec tl(nonempty_maybe_improper_list(elem, last)) ::
  maybe_improper_list(elem, last) | last
when elem: term(), last: term()

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

Хвост списка — это список без первого элемента.

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

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

Примеры

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

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

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

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

При передаче пустого списка генерируется:

tl([])
** (ArgumentError) argument error

trunc(number)Source

@spec trunc(number()) :: integer()

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

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

Примеры

iex> trunc(5.4)
5

iex> trunc(-5.99)
-5

iex> trunc(-5)
-5

tuple_size(tuple)Source

@spec tuple_size(tuple()) :: non_neg_integer()

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

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

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

Примеры

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

Функции

left && rightSource

Оператор логического "и".

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

base ** exponentSource

@spec integer() ** non_neg_integer() :: integer()
@spec integer() ** neg_integer() :: float()
@spec float() ** float() :: float()
@spec integer() ** float() :: float()
@spec float() ** integer() :: float()

Оператор возведения в степень.

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

Примеры

iex> 2 ** 2
4
iex> 2 ** -4
0.0625

iex> 2.0 ** 2
4.0
iex> 2 ** 2.0
4.0

left ++ rightSource

@spec [] ++ a :: a when a: term()
@spec [...] ++ term() :: maybe_improper_list()

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

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

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

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

Примеры

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

iex> ~c"foo" ++ ~c"bar"
~c"foobar"

# a non-list on the right will return an improper list
# with said element at the end
iex> [1, 2] ++ 3
[1, 2 | 3]
iex> [1, 2] ++ {3, 4}
[1, 2 | {3, 4}]

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

# empty list on the left will return the right operand
iex> [] ++ 1
1

Оператор ++/2 является правоассоциативным, что означает:

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

Что эквивалентно:

iex> [1, 2, 3] -- ([1] ++ [2])
[3]

left -- rightSource

@spec list() -- list() :: list()

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

Эта функция оптимизирована, поэтому сложность a -- b пропорциональна length(a) * log(length(b)). См. также руководство по эффективности Erlang.

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

Примеры

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

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

Оператор --/2 является правоассоциативным, что означает:

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

Что эквивалентно:

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

..Source

Создаёт диапазон 0..-1//1.

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

  • При перечислении он пустой

  • При использовании как slice, он возвращает срезанный элемент как есть

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

Примеры

iex> Enum.to_list(..)
[]

iex> String.slice("Hello world!", ..)
"Hello world!"

first..lastSource

Создаёт диапазон от first до last.

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

Если first больше last, диапазон будет убывающим от first до last, хотя такое поведение устарело. Вместо этого используйте явное указание шага с first..last//-1.

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

Примеры

iex> 0 in 1..3
false
iex> 2 in 1..3
true

iex> Enum.to_list(1..3)
[1, 2, 3]

first..last//stepSource

Создаёт диапазон от first до last с шагом step.

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

Примеры

iex> 0 in 1..3//1
false
iex> 2 in 1..3//1
true
iex> 2 in 1..3//2
false

iex> Enum.to_list(1..3//1)
[1, 2, 3]
iex> Enum.to_list(1..3//2)
[1, 3]
iex> Enum.to_list(3..1//-1)
[3, 2, 1]
iex> Enum.to_list(1..0//1)
[]

!valueSource

Оператор логического "не".

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

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

Примеры

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

iex> !List.first([])
true

left <> rightSource

Оператор бинарной конкатенации. Объединяет две бинарные строки.

Генерирует исключение ArgumentError, если одна из сторон не является бинарной строкой.

Примеры

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

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

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

x <> "bar" = "foobar" приведёт к исключению ArgumentError.

left =~ rightSource

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

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

Если справа стоит регулярное выражение, возвращает true если left соответствует справа.

Если справа стоит строка, возвращает true если left содержит right.

Примеры

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

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

iex> "abcd" =~ ~r//
true

iex> "abcd" =~ "bc"
true

iex> "abcd" =~ "ad"
false

iex> "abcd" =~ "abcd"
true

iex> "abcd" =~ ""
true

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

@exprSource

Унарный оператор атрибута модуля.

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

Типичный пример атрибутов - помечать, что модуль реализует поведение 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.

Префикс атрибутов модуля

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

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

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

Внимание! Несколько ссылок на один и тот же атрибут

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

@files %{
  example1: File.read!("lib/example1.data"),
  example2: File.read!("lib/example2.data")
}

def example1, do: @files[:example1]
def example2, do: @files[:example2]

В приведенном выше примере каждая ссылка на @files может привести к полной и отдельной копии всего атрибута модуля @files. Вместо этого, ссылайтесь на атрибут модуля один раз в приватной функции:

@files %{
  example1: File.read!("lib/example1.data"),
  example2: File.read!("lib/example2.data")
}

defp files(), do: @files
def example1, do: files()[:example1]
def example2, do: files()[:example2]

alias!(alias)Source

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

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

END_OF_DOCUMENT_MARKER

apply(fun, args)Source

@spec apply(fun(), [any()]) :: any()

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

Если количество аргументов известно на этапе компиляции, предпочтительнее использовать fun.(arg_1, arg_2, ..., arg_n), так как оно более наглядно, чем apply(fun, [arg_1, arg_2, ..., arg_n]).

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

Примеры

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

apply(module, function_name, args)Source

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

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

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

Если количество аргументов и имя функции известны на этапе компиляции, предпочтительнее использовать module.function(arg_1, arg_2, ..., arg_n), так как оно более наглядно, чем apply(module, :function, [arg_1, arg_2, ..., arg_n]).

apply/3 не может использоваться для вызова частных функций.

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

Примеры

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

binary_slice(binary, range)Source

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

Если начало или конец диапазона отрицательны, они преобразуются в положительные индексы, основанные на размере двоичной строки. Например, -1 означает последний байт двоичной строки.

Аналогично binary_part/3, но работает с диапазонами и не допускается в охранных выражениях.

Функция работает с байтами. Для операции среза, которая учитывает символы, см. String.slice/2.

Примеры

iex> binary_slice("elixir", 0..5)
"elixir"
iex> binary_slice("elixir", 1..3)
"lix"
iex> binary_slice("elixir", 1..10)
"lixir"

iex> binary_slice("elixir", -4..-1)
"ixir"
iex> binary_slice("elixir", -4..6)
"ixir"
iex> binary_slice("elixir", -10..10)
"elixir"

Для диапазонов, где start > stop, необходимо явно обозначить их как возрастающие:

iex> binary_slice("elixir", 2..-1//1)
"ixir"
iex> binary_slice("elixir", 1..-2//1)
"lixi"

Вы можете использовать ../0 в качестве сокращения для 0..-1//1, которое возвращает всю двоичную строку как есть:

iex> binary_slice("elixir", ..)
"elixir"

Шаг может быть любым положительным числом. Например, чтобы получить каждый 2-й символ двоичной строки:

iex> binary_slice("elixir", 0..-1//2)
"eii"

Если первая позиция находится за пределами строки или после последней позиции диапазона, возвращается пустая строка:

iex> binary_slice("elixir", 10..3//1)
""
iex> binary_slice("elixir", -10..-7)
""
iex> binary_slice("a", 1..1500)
""

binary_slice(binary, start, size)Source

Возвращает двоичную строку, начинающуюся со смещения start и заданного size.

Аналогично binary_part/3, за исключением того, что если start + size больше размера двоичной строки, оно автоматически обрезается до размера двоичной строки вместо повышения исключения. В отличие от binary_part/3, эта функция не допускается в охранных выражениях.

Функция работает с байтами. Для операции среза, которая учитывает символы, см. String.slice/3.

Примеры

iex> binary_slice("elixir", 0, 6)
"elixir"
iex> binary_slice("elixir", 0, 5)
"elixi"
iex> binary_slice("elixir", 1, 4)
"lixi"
iex> binary_slice("elixir", 0, 10)
"elixir"

Если start отрицательно, оно нормализуется относительно размера двоичной строки и ограничено 0:

iex> binary_slice("elixir", -3, 10)
"xir"
iex> binary_slice("elixir", -10, 10)
"elixir"

Если size равно нулю, возвращается пустая двоичная строка:

iex> binary_slice("elixir", 1, 0)
""

Если start больше или равно размеру двоичной строки, возвращается пустая двоичная строка:

iex> binary_slice("elixir", 10, 10)
""

binding(context \\ nil)Source

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

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

Если заданный 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]

dbg(code \\ quote do binding() end, options \\ [])Source

Отлаживает данный code.

dbg/2 может использоваться для отладки данного code через настраиваемую функцию отладки. Возвращает результат данного кода.

Примеры

Рассмотрим этот вызов dbg/2:

dbg(Atom.to_string(:debugging))
#=> "debugging"

Возвращает строку "debugging", которая является результатом вызова Atom.to_string/1. Кроме того, вышеприведенный вызов выводит:

[my_file.ex:10: MyMod.my_fun/0]
Atom.to_string(:debugging) #=> "debugging"

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

"Elixir is cool!"
|> String.trim_trailing("!")
|> String.split()
|> List.first()
|> dbg()
#=> "Elixir"

Приведенный выше код выводит:

[my_file.ex:10: MyMod.my_fun/0]
"Elixir is cool!" #=> "Elixir is cool!"
|> String.trim_trailing("!") #=> "Elixir is cool"
|> String.split() #=> ["Elixir", "is", "cool"]
|> List.first() #=> "Elixir"

Без аргументов, dbg() отлаживает информацию о текущей привязке. См. binding/1.

dbg в IEx

Вы можете настроить IEx на замену dbg своим бэкендом IEx.pry/0 с помощью вызова:

$ iex --dbg pry

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

Если вы вызываете dbg в конце конвейера (используя |> ) в IEx, вы можете пройти по каждой ступени конвейера поочередно, введя "next" (или "n").

Обратите внимание dbg поддерживает пошаговое выполнение только для конвейеров (т.е. оно может выполнить пошаговое выполнение только для кода, который видит). Для общего пошагового выполнения вы можете установить точки останова, используя IEx.break!/4.

Для получения более подробной информации, см. документацию IEx.

Настройка функции отладки

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

Функция отладки может настраиваться во время компиляции через ключ :dbg_callback приложения :elixir. Функция отладки должна быть {module, function, args} кортежем. Функция function в module будет вызвана с тремя аргументами, предваряющими args:

  1. AST выражения code
  2. AST выражения options
  3. Среда Macro.Env вызова dbg/2

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

Вот простой пример:

defmodule MyMod do
  def debug_fun(code, options, caller, device) do
    quote do
      result = unquote(code)
      IO.inspect(unquote(device), result, label: unquote(Macro.to_string(code)))
    end
  end
end

Для настройки функции отладки:

# In config/config.exs
config :elixir, :dbg_callback, {MyMod, :debug_fun, [:stdio]}

Функция отладки по умолчанию

По умолчанию используемая функция отладки — Macro.dbg/3. Она просто выводит информацию о коде в стандартный вывод и возвращает значение, возвращённое при вычислении code. options используются для управления тем, как проверяются термины. Это те же параметры, что и у inspect/2.

def(call, expr \\ nil)Source

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

Примеры

defmodule Foo do
  def bar, do: :baz
end

Foo.bar()
#=> :baz

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

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

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

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

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

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

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

MyMath.multiply_by(4)
#=> 8

Компилятор преобразует это в несколько функций с разными арностью, здесь MyMath.multiply_by/1 и MyMath.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

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

Списки ключевых слов с аргументами по умолчанию

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

defmodule MyConfiguration do
  @default_opts [storage: "local"]

  def configure(resource, opts \\ []) do
    opts = Keyword.merge(@default_opts, opts)
    storage = opts[:storage]
    # ...
  end
end

Разница между использованием Map и Keyword для хранения многих аргументов заключается в ключах Keyword:

  • должны быть атомами
  • могут быть заданы более одного раза
  • упорядочены, как указано разработчиком

Имена функций

Имена функций и переменных в Elixir должны начинаться с подчеркивания или буквы Юникода, которая не является заглавной или строчной. Они могут продолжаться с последовательностью букв Юникода, цифр и подчеркиваний. Они могут заканчиваться ? или !. Стили руководства по именованию Elixir предлагают для имен функций и переменных формат snake_case.

rescue/catch/after/else

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

def convert(number) do
  try do
    String.to_integer(number)
  rescue
    e in ArgumentError -> {:error, e.message}
  end
end

def convert(number) do
  String.to_integer(number)
rescue
  e in ArgumentError -> {:error, e.message}
end

defdelegate(funs, opts)Source

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

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

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

Исключения — это структуры, поддерживаемые модулем, реализующим поведение 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)Source

@spec defguard(Macro.t()) :: Macro.t()

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

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

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

Пример

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

@spec defguardp(Macro.t()) :: Macro.t()

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

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

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

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

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

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

defmacro(call, expr \\ nil)Source

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

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

См. 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)Source

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

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

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

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

END_OF_DOCUMENT_MARKER

defmodule(alias, do_block)Source

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

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

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

Примеры

defmodule Number do
  def one, do: 1
  def two, do: 2
end
#=> {:module, Number, <<70, 79, 82, ...>>, {:two, 0}}

Number.one()
#=> 1

Number.two()
#=> 2

Имена модулей и псевдонимы

Имена модулей (и псевдонимы) должны начинаться с заглавной ASCII буквы, за которой могут следовать любые ASCII буквы, цифры или символы подчеркивания. Конвенции именования Elixir для имён модулей и псевдонимов рекомендуют использовать формат CamelCase.

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

Вложенность

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

defmodule Foo do
  defmodule Bar do
  end
end

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

Если модуль Foo.Bar перемещён в другое место, ссылки на Bar в модуле Foo необходимо обновить до полных квалифицированных имён (Foo.Bar) или явно установить псевдоним в модуле Foo с помощью 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 модулей могут быть динамически сгенерированы. Это очень полезно при работе с макросами. Например, можно написать:

defmodule Module.concat(["Foo", "Bar"]) 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)Source

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

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

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

Макросы не могут быть переопределены как функции и наоборот.

Пример

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

      defoverridable test: 2
    end
  end
end

defmodule ChildMod do
  use DefaultMod

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

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

Предупреждение

Используйте defoverridable с осторожностью. Если вам нужно определить несколько модулей с одинаковым поведением, лучше перенести реализацию по умолчанию в вызывающую сторону и проверить существование обратного вызова с помощью Code.ensure_loaded?/1 и function_exported?/3.

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

defmodule CallsTest do
  def receives_module_and_calls_test(module, x, y) do
    if Code.ensure_loaded?(module) and function_exported?(module, :test, 2) do
      module.test(x, y)
    else
      x + y
    end
  end
end

Пример с поведением

Вы также можете передать поведение в defoverridable и оно пометит все обратные вызовы в поведении как переопределимые:

defmodule Behaviour do
  @callback test(number(), number()) :: number()
end

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

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

      defoverridable Behaviour
    end
  end
end

defmodule ChildMod do
  use DefaultMod

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

defp(call, expr \\ nil)Source

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

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

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

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

defstruct(fields)Source

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

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

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

Примеры

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

Добавьте документацию к структуре с атрибутом @doc, как и для функции.

defmodule Post do
  @doc "A post. The content should be valid Markdown."
  defstruct [:title, :content, :author]
end

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

%Post{title: "Hello world!"}

Для получения более подробной информации о создании, обновлении и сопоставлении шаблонов со структурами, см. %/2.

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

Хотя структуры являются отображениями, по умолчанию структуры не реализуют ни один из протоколов, реализованных для отображений. Например, попытка использовать протокол с структурой 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: nil
end

MyProtocol.call(john) # it works!

Частый пример — реализация протокола Inspect, чтобы скрыть определённые поля при выводе структуры:

defmodule User do
  @derive {Inspect, only: :name}
  defstruct name: nil, age: nil
end

Для каждого протокола в @derive, Elixir проверит, реализован ли протокол обратный вызов Protocol.__deriving__/2. Если это так, вызов будет выполнен, и он должен определить модуль реализации. В противном случае автоматически наследуется реализация, которая просто указывает на реализацию Any. Для получения дополнительной информации, см. 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.

END_OF_DOCUMENT_MARKER

destructure(left, right)Source

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

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

Примеры

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

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

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

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

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

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

Примеры

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

iex> function_exported?(Enum, :map, 10)
false

iex> function_exported?(List, :to_string, 1)
true

get_and_update_in(path, fun)Source

Получает значение и обновляет вложенную структуру данных с помощью заданной 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})

Это также работает со вложенными структурами и способом задания путей struct.path.to.value:

get_and_update_in(struct.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)Source

@spec get_and_update_in(
  structure,
  keys,
  (term() | nil -> {current_value, new_value} | :pop)
) :: {current_value, new_structure :: structure}
when structure: Access.t(),
     keys: [term(), ...],
     current_value: Access.value(),
     new_value: Access.value()

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

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

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

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

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

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

Примеры

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

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

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

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> get_and_update_in(users, ["jane", :age], &{&1, &1 + 1})
** (ArgumentError) could not put/update key :age on a nil value

Функции в качестве ключей

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

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

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

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

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

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

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

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

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

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

Кроме того, этот макрос может обходить структуры:

get_in(struct.foo.bar)

Если какой-либо из ключей возвращает nil, то будет возвращено nil, и get_in/1 не будет продолжать обход.

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

Примеры

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

get_in(data, keys)Source

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

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

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

Примеры

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

get_in/2 также может использовать аксессоры в модуле Access для обхода более сложных структур данных. Например, здесь мы используем Access.all/0 для обхода списка:

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

В случае, если любой из компонентов возвращает nil, будет возвращено nil, и get_in/2 не будет продолжать обход:

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

Функции в качестве ключей

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

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

Это означает, что get_in/2 можно расширить для предоставления пользовательских поисков. Именно так и ведет себя ключ Access.all/0 в предыдущем разделе. Например, мы можем вручную реализовать такой обход следующим образом:

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]

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

Работа со структурами

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

get_in(some_struct, [:some_key, :nested_key])

Есть две альтернативы. Поскольку у структур есть предопределенные ключи, мы можем использовать обозначение struct.field:

some_struct.some_key.nested_key

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

get_in(some_struct.some_key.nested_key)

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

case some_struct do
  %{some_key: %{nested_key: value}} -> value
  %{} -> nil
end

if(condition, clauses)Source

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

Этот макрос ожидает, что первый аргумент будет условием, а второй — списком ключевых слов. В целом, разработчики Elixir предпочитают использовать сопоставление с образцом и условия в определениях функций и case/2, так как они лаконичны и точны. Однако не все условия можно выразить с помощью шаблонов и условий, что делает if/2 жизнеспособной альтернативой.

Аналогично case/2, любые присваивания в условии будут доступны в обоих ветвях, а также после выражения if.

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

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

@spec 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(~c"bar")
"~c\"bar\""

iex> inspect([0 | ~c"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)Source

@spec 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()Source

@spec make_ref() :: reference()

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

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

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

Примеры

make_ref()
#=> #Reference<0.0.0.135>

match?(pattern, expr)Source

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

Примеры

iex> match?(1, 1)
true

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]

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

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

Значения и шаблоны

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

# don't do this
pattern = %{a: :a}
match?(pattern, %{b: :b})

Аналогично, перемещение выражения из шаблона может больше не сохранять его семантику. Например:

match?([_ | _], [1, 2, 3])
#=> true

pattern = [_ | _]
match?(pattern, [1, 2, 3])
** (CompileError) invalid use of _. _ can only be used inside patterns to ignore values and cannot be used in expressions. Make sure you are inside a pattern or change it accordingly

Другой пример состоит в том, что карта как шаблон выполняет частичное соответствие, но не после присвоения переменной:

match?(%{x: 1}, %{x: 1, y: 2})
#=> true

attrs = %{x: 1}
match?(^attrs, %{x: 1, y: 2})
#=> false

Оператор pin проверит, равны ли значения, используя ===/2, в то время как шаблоны имеют свои правила при сопоставлении карт, списков и т. д. Такое поведение не является специфичным для match?/2. Следующий код также генерирует исключение:

attrs = %{x: 1}
^attrs = %{x: 1, y: 2}
#=> (MatchError) no match of right hand side value: %{x: 1, y: 2}

max(first, second)Source

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

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

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

Это выполняет структурное сравнение, где все термины Elixir могут быть сравнины друг с другом. Дополнительную информацию см. в разделе «Структурное сравнение».

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

Примеры

iex> max(1, 2)
2
iex> max("a", "b")
"b"
END_OF_DOCUMENT_MARKER

min(first, second)Source

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

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

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

Это выполняет структурное сравнение, где все термины Elixir могут быть сравнены друг с другом. Подробнее см. раздел "Структурное сравнение".

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

Примеры

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

pop_in(path)Source

Удаляет ключ из вложенной структуры по заданному 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)Source

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

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

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

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

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

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

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

Это также работает с вложенными структурами и способом указания путей через struct.path.to.value:

put_in(struct.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)Source

@spec 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, произойдет ошибка:

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> put_in(users, ["jane", :age], "oops")
** (ArgumentError) could not put/update key :age on a nil value

raise(message)Source

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

Если message является строкой, она вызывает исключение RuntimeError с этим значением.

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

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

Если message — что-то другое, 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)Source

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

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

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

Примеры

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

reraise(message, stacktrace)Source

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

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

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

Примеры

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

reraise(exception, attributes, stacktrace)Source

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

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

Примеры

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

send(dest, message)Source

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

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

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

Дополнительная документация доступна в ! оператор Erlang документации.

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

Примеры

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

sigil_C(term, modifiers)Source

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

Возвращает charlist без интерполяций и без управляющих последовательностей.

Charlist — это список целых чисел, где все целые числа являются допустимыми кодовыми точками. Три выражения ниже эквивалентны:

~C"foo\n"
[?f, ?o, ?o, ?\\, ?n]
[102, 111, 111, 92, 110]

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

Примеры

iex> ~C(foo)
~c"foo"

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

iex> ~C(foo\n)
~c"foo\\n"

sigil_c(term, modifiers)Source

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

Возвращает charlist, разыменовывая символы и заменяя интерполяции.

Charlist — это список целых чисел, где все целые числа являются допустимыми кодовыми точками. Три выражения ниже эквивалентны:

~c"foo"
[?f, ?o, ?o]
[102, 111, 111]

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

Примеры

iex> ~c(foo)
~c"foo"

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

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

Список будет напечатан как сигил ~c только если все кодовые точки находятся в диапазоне ASCII:

iex> ~c"hełło"
[104, 101, 322, 322, 111]

iex> [104, 101, 108, 108, 111]
~c"hello"

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

sigil_D(date_string, modifiers)Source

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

По умолчанию, этот сигил использует встроенный Calendar.ISO, который требует, чтобы даты были записаны в формате ISO8601:

~D[yyyy-mm-dd]

например:

~D[2015-01-13]

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

~D[SOME-REPRESENTATION My.Alternative.Calendar]

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

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

Примеры

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

sigil_N(naive_datetime_string, modifiers)Source

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

По умолчанию, этот сигил использует встроенный Calendar.ISO, который требует, чтобы неявные даты и время были записаны в формате ISO8601:

~N[yyyy-mm-dd hh:mm:ss]
~N[yyyy-mm-dd hh:mm:ss.ssssss]
~N[yyyy-mm-ddThh:mm:ss.ssssss]

например:

~N[2015-01-13 13:00:07]
~N[2015-01-13T13:00:07.123]

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

~N[SOME-REPRESENTATION My.Alternative.Calendar]

Вариант ~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)Source

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

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

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

Примеры

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

iex> Regex.match?(~r/a#{:b}c/, "abc")
true

Хотя сигил ~r позволяет использовать круглые и квадратные скобки в качестве разделителей, предпочтительнее использовать " или /, чтобы избежать конфликтов при экранировании со служебными символами регулярных выражений.

sigil_S(term, modifiers)Source

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

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

Примеры

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

sigil_s(term, modifiers)Source

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

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

Примеры

iex> ~s(foo)
"foo"

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

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

sigil_T(time_string, modifiers)Source

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

По умолчанию, этот сигил использует встроенный Calendar.ISO, который требует, чтобы время было записано в формате ISO8601:

~T[hh:mm:ss]
~T[hh:mm:ss.ssssss]

например:

~T[13:00:07]
~T[13:00:07.123]

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

~T[SOME-REPRESENTATION My.Alternative.Calendar]

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

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

Примеры

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

sigil_U(datetime_string, modifiers)Source

Обрабатывает сигил ~U для создания UTC DateTime.

По умолчанию, этот сигил использует встроенный Calendar.ISO, который требует, чтобы UTC даты и время были записаны в формате ISO8601:

~U[yyyy-mm-dd hh:mm:ssZ]
~U[yyyy-mm-dd hh:mm:ss.ssssssZ]
~U[yyyy-mm-ddThh:mm:ss.ssssss+00:00]

например:

~U[2015-01-13 13:00:07Z]
~U[2015-01-13T13:00:07.123+00:00]

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

~U[SOME-REPRESENTATION My.Alternative.Calendar]

Указанное datetime_string должно содержать смещение "Z" или "00:00", которое отмечает его как UTC, в противном случае возникает ошибка.

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

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

Примеры

iex> ~U[2015-01-13 13:00:07Z]
~U[2015-01-13 13:00:07Z]
iex> ~U[2015-01-13T13:00:07.001+00:00]
~U[2015-01-13 13:00:07.001Z]

sigil_W(term, modifiers)Source

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

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

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

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

Примеры

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

sigil_w(term, modifiers)Source

Обрабатывает сигил ~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]

iex> ~w(foo bar baz)c
[~c"foo", ~c"bar", ~c"baz"]

spawn(fun)Source

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

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

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

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

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

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

Примеры

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

spawn_link(fun)Source

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

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

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

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

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

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

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

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

Примеры

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

struct(struct, fields \\ [])Source

@spec struct(module() | struct(), Enumerable.t()) :: struct()

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

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

Ключи в Enumerable, которые не существуют в структуре, автоматически отбрасываются. Обратите внимание, что ключи должны быть атомами, так как при определении структуры разрешены только атомы. Если в Enumerable есть дублирующиеся ключи, будет взят последний элемент (поведение аналогично Map.new/1).

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

@spec struct!(module() | struct(), Enumerable.t()) :: struct()

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

Функция struct!/2 эмулирует поведение структур во время компиляции. Это означает, что:

  • при создании структуры, как в struct!(SomeStruct, key: :value), она эквивалентна %SomeStruct{key: :value}, и поэтому эта функция будет проверять, принадлежит ли каждая пара ключ-значение структуре. Если структура накладывает какие-либо ограничения на ключи через @enforce_keys, они также будут применены;

  • при обновлении структуры, как в struct!(%SomeStruct{}, key: :value), она эквивалентна %SomeStruct{struct | key: :value}, и поэтому эта функция будет проверять, принадлежит ли каждая пара ключ-значение структуре.

tap(value, fun)Source

Передает первый аргумент, value, во второй аргумент, функцию fun, и возвращает value само.

Полезна для выполнения синхронных побочных эффектов в конвейере, используя оператор |>/2.

Примеры

iex> tap(1, fn x -> x + 1 end)
1

Чаще всего используется в конвейерах, используя оператор |>/2. Например, предположим, что вы хотите проверить часть структуры данных. Вы можете написать:

%{a: 1}
|> Map.update!(:a, & &1 + 2)
|> tap(&IO.inspect(&1.a))
|> Map.update!(:a, & &1 * 2)

then(value, fun)Source

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

Другими словами, она вызывает функцию fun с value в качестве аргумента и возвращает её результат.

Чаще всего используется в конвейерах, используя оператор |>/2, позволяя вам передать значение в функцию вне её первого аргумента.

Примеры

iex> 1 |> then(fn x -> x * 2 end)
2

iex> 1 |> then(fn x -> Enum.drop(["a", "b", "c"], x) end)
["b", "c"]

throw(term)Source

@spec throw(term()) :: no_return()

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

Использование throw/1 обычно не рекомендуется, так как оно позволяет функции выйти за пределы обычного потока выполнения, что затрудняет чтение кода. Кроме того, все брошенные значения должны быть перехвачены try/catch. См. try/1 для получения дополнительной информации.

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

to_charlist(term)Source

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

Примеры

iex> to_charlist(:foo)
~c"foo"

to_string(term)Source

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

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

Примеры

iex> to_string(:foo)
"foo"

to_timeout(duration)Source

@spec to_timeout([{unit, non_neg_integer()}] | timeout() | Duration.t()) :: timeout()
when unit: :week | :day | :hour | :minute | :second | :millisecond

Создаёт таймаут в миллисекундах из заданных компонентов, длительности или таймаута.

Эта функция полезна для построения таймаутов для использования в функциях, которые ожидают значения timeout/0 (например, Process.send_after/4 и многие другие).

Аргумент

Аргумент duration может быть одним из Duration, timeout/0 или списком компонентов. Каждый из них описан ниже.

Передача значений Duration

Duration.t/0 структуры могут быть преобразованы в таймауты. У заданной длительности должны быть установлены поля year и month в 0, поскольку их нельзя надёжно преобразовать в миллисекунды (из-за переменного числа дней в месяце и году).

Микросекунды в длительностях преобразуются в миллисекунды (через System.convert_time_unit/3).

Передача компонентов

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

  • :week - количество недель (неделя всегда 7 дней)
  • :day - количество дней (день всегда 24 часа)
  • :hour - количество часов
  • :minute - количество минут
  • :second - количество секунд
  • :millisecond - количество миллисекунд

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

Передача таймаутов

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

Примеры

С помощью списка ключевых слов:

iex> to_timeout(hour: 1, minute: 30)
5400000

С помощью длительности:

iex> to_timeout(%Duration{hour: 1, minute: 30})
5400000

С помощью таймаута:

iex> to_timeout(5400000)
5400000
iex> to_timeout(:infinity)
:infinity

unless(condition, clauses)Source

Этот макрос устарел. Используйте if/2 вместо него.

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

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

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

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

update_in(opts[:foo][:bar], &(&1 + 1))

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

update_in(opts, [:foo, :bar], &(&1 + 1))

Это также работает с вложенными структурами и способом указания путей struct.path.to.value:

update_in(struct.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)Source

@spec update_in(Access.t(), [term(), ...], (term() -> term())) :: Access.t()

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

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

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

Примеры

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

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

iex> users = %{"john" => %{age: 27}, "meg" => %{age: 23}}
iex> update_in(users, ["jane", :age], & &1 + 1)
** (ArgumentError) could not put/update key :age on a nil value

use(module, opts \\ [])Source

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

При вызове:

use MyModule, some: :options

Elixir вызовет MyModule.__using__/1 , передав второй аргумент use в качестве своего аргумента. Поскольку __using__/1 обычно является макросом, применяются все обычные правила макросов, и его возвращаемое значение должно быть цитируемым кодом, который затем вставляется туда, где вызывается use/2.

Вставка кода

use MyModule работает как точка вставки кода в вызывающий модуль. Учитывая, что вызывающий модуль имеет небольшой контроль над тем, как вставляется код, use/2 следует использовать с осторожностью. Если возможно, избегайте использования в пользу import/2 или alias/2 всякий раз, когда это возможно.

Примеры

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

defmodule AssertionTest do
  use ExUnit.Case, async: true

  test "always pass" do
    assert true
  end
end

В этом примере Elixir вызовет макрос __using__/1 в модуле ExUnit.Case с аргументом [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

Рекомендации по использованию

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

use/2 также может использоваться для алиасирования, требования или импорта функциональности из других модулей:

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.

Учитывая, что use MyModule может генерировать любой код, разработчикам может быть сложно понять влияние use MyModule.

По этой причине, для предоставления руководства и ясности, рекомендуется включать блок предупреждения в свой @moduledoc , который объясняет, как use MyModule влияет на их код. Например, документация GenServer описывает:

use GenServer

Когда вы use GenServer, модуль GenServer установит @behaviour GenServer и определит функцию child_spec/1 , так что ваш модуль может использоваться как дочерний в дереве контроля.

Это предоставляет краткое изложение того, как использование модуля влияет на код пользователя. Имейте в виду, что следует указывать только изменения в общедоступном API модуля. Например, если use MyModule устанавливает внутренний атрибут, называемый @_my_module_info , и этот атрибут никогда не предназначен для публичного использования, его не следует указывать.

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

> #### `use GenServer` {: .info}
>
> When you `use GenServer`, the GenServer module will
> set `@behaviour GenServer` and define a `child_spec/1`
> function, so your module can be used as a child
> in a supervision tree.

var!(var, context \\ nil)Source

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

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

Примеры

iex> Kernel.var!(example) = 1
1
iex> Kernel.var!(example)
1

слева |> справаИсходный код

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

Этот оператор вводит выражение слева в качестве первого аргумента вызова функции справа.

Примеры

iex> [1, [2], 3] |> List.flatten()
[1, 2, 3]

Приведённый выше пример эквивалентен вызову List.flatten([1, [2], 3]).

Оператор |>/2 в основном полезен, когда требуется выполнить серию операций, напоминающих конвейер:

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

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

some_fun = &Regex.replace(~r/l/, &1, "L")
"Hello" |> some_fun.()

В качестве альтернативы можно использовать then/2 для того же эффекта:

some_fun = &Regex.replace(~r/l/, &1, "L")
"Hello" |> then(some_fun)

then/2 чаще всего используется, когда вы хотите применить конвейер к функции, но значение ожидается вне первого аргумента, как показано выше. Заменив some_fun его значением, мы получим:

"Hello" |> then(&Regex.replace(~r/l/, &1, "L"))

слева || справаИсходный код

Логический оператор "или".

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

Скачать версию ePub

Создано с помощью ExDoc (v0.36.1) для языка программирования Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/Kernel.html

Spec-Zone.ru

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