Исходный код Ядро
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.
- 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)
Удобный макрос, проверяющий, совпадает ли правая часть (выражение) с левой частью (шаблоном).
- 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для создания UTCDateTime.- 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
слева и справаИсходный код
Оператор "и" строго для булевых значений.
Если 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.
Допускается в тестах-стражах. Встраивается компилятором.
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 для получения дополнительной информации.
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:
Функция отладки вызывается во время компиляции, и она также должна вернуть 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 для правил именования и аргументов по умолчанию.
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.
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, возвращаетсяnilfoo.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" 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])
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
Предоставляет макрос 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, этот оператор принимает любое выражение в качестве первого аргумента, а не только логические значения.
© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/Kernel.html