Изменения в Elixir v1.14
Elixir v1.14 содержит множество улучшений в опыте отладки в Elixir и инспекции типов данных. Он также включает новую абстракцию для простого разбиения процессов, называемую PartitionSupervisor, а также улучшенные времена компиляции и сообщения об ошибках.
Elixir v1.14 — последняя версия, поддерживающая Erlang/OTP 23. Рассмотрите обновление до Erlang/OTP 24 или Erlang/OTP 25.
dbg
Kernel.dbg/2 — новая макрокоманда, которая чем-то похожа на IO.inspect/2, но специально разработана для отладки.
При вызове она выводит значение того, что вы ей передаёте, а также отлаживаемый код и его расположение. Этот код:
# In my_file.exs
feature = %{name: :dbg, inspiration: "Rust"}
dbg(feature)
dbg(Map.put(feature, :in_version, "1.14.0"))
Выводит это:
$ elixir my_file.exs
[my_file.exs:2: (file)]
feature #=> %{inspiration: "Rust", name: :dbg}
[my_file.exs:3: (file)]
Map.put(feature, :in_version, "1.14.0") #=> %{in_version: "1.14.0", inspiration: "Rust", name: :dbg}
dbg/2 может сделать больше. Это макрокоманда, поэтому она понимает код Elixir. Вы можете увидеть это, передавая последовательность |> конвейеров ей. dbg/2 выведет значение для каждого шага конвейера. Этот код:
# In dbg_pipes.exs
__ENV__.file
|> String.split("/", trim: true)
|> List.last()
|> File.exists?()
|> dbg()
Выводит это:
$ elixir dbg_pipes.exs
[dbg_pipes.exs:5: (file)]
__ENV__.file #=> "/home/myuser/dbg_pipes.exs"
|> String.split("/", trim: true) #=> ["home", "myuser", "dbg_pipes.exs"]
|> List.last() #=> "dbg_pipes.exs"
|> File.exists?() #=> true
IEx и отладка
dbg/2 поддерживает настраиваемые бэкэнды. IEx автоматически заменяет стандартный бэкэнд на бэкэнд, который останавливает выполнение кода с помощью IEx.Pry, предоставляя разработчикам возможность доступа к локальным переменным, импортам и другим данным. Это также работает с конвейерами: если вы передадите серию |> вызовов конвейера dbg (или передадите в него в конце, как |> dbg()), вы сможете шаг за шагом проходить через каждую строку конвейера.
Вы можете сохранить стандартное поведение, передав параметр --no-pry в IEx.
PartitionSupervisor
PartitionSupervisor — новый модуль, реализующий новый тип супервайзера. Супервайзер разделения предназначен для ситуаций, когда у вас есть один контролируемый процесс, который становится узким местом. Если состояние этого процесса можно легко разделить, то вы можете использовать PartitionSupervisor, чтобы контролировать несколько изолированных копий этого процесса, выполняемых параллельно, каждая из которых назначена своей собственной части.
Например, представьте, что у вас есть процесс ErrorReporter, который вы используете для отчётности об ошибках в службу мониторинга.
# Application supervisor: children = [ # ..., ErrorReporter ] Supervisor.start_link(children, strategy: :one_for_one)
По мере увеличения конкуретности вашей программы, процесс ErrorReporter может получать запросы от многих других процессов и, в конечном итоге, стать узким местом. В таком случае может помочь запустить несколько копий процесса ErrorReporter под PartitionSupervisor.
# Application supervisor
children = [
{PartitionSupervisor, child_spec: ErrorReporter, name: Reporters}
]
Супервайзер PartitionSupervisor запустит количество процессов, равное System.schedulers_online() по умолчанию (чаще всего по одному на ядро). Теперь, когда маршрутизируются запросы к процессам ErrorReporter, мы можем использовать кортеж :via и направлять запросы через супервайзер разделения.
partitioning_key = self()
ErrorReporter.report({:via, PartitionSupervisor, {Reporters, partitioning_key}}, error)
Использование self() в качестве ключа разбиения означает, что один и тот же процесс всегда будет сообщать об ошибках одному и тому же процессу ErrorReporter, обеспечивая форму обратной связи. Вы можете использовать любой термин в качестве ключа разбиения.
Общий пример
Общий и практический пример использования PartitionSupervisor — разбиение чего-то вроде DynamicSupervisor. При запуске большого количества процессов под ним динамический супервайзер может стать узким местом, особенно если этим процессам требуется длительное время для инициализации. Вместо запуска одного DynamicSupervisor, можно запустить несколько:
children = [
{PartitionSupervisor, child_spec: DynamicSupervisor, name: MyApp.DynamicSupervisors}
]
Supervisor.start_link(children, strategy: :one_for_one)
Теперь вы запускаете процессы в динамическом супервайзере для соответствующего раздела. Например, вы можете разделить по PID, как в предыдущем примере:
DynamicSupervisor.start_child(
{:via, PartitionSupervisor, {MyApp.DynamicSupervisors, self()}},
my_child_specification
)
Улучшенные сообщения об ошибках при работе с двоичными данными и оценке
Erlang/OTP 25 улучшил сообщения об ошибках при построении и оценке двоичных данных. Эти улучшения применяются и к Elixir. До версии v1.14 сообщения об ошибках при построении двоичных данных часто были неотлаживаемыми общими «ошибками аргументов». С Erlang/OTP 25 и Elixir v1.14 предоставляется более подробная информация для более лёгкой отладки. Эта работа является частью EEP 54.
Ранее:
int = 1 bin = "foo" int <> bin #=> ** (ArgumentError) argument error
Сейчас:
int = 1 bin = "foo" int <> bin #=> ** (ArgumentError) construction of binary failed: #=> segment 1 of type 'binary': #=> expected a binary but got: 1
Нарезка с шагом
В Elixir v1.12 были представлены диапазоны с шагом, которые являются диапазонами, где вы можете указать «шаг»:
Enum.to_list(1..10//3) #=> [1, 4, 7, 10]
Диапазоны с шагом особенно полезны для числовых операций, включающих векторы и матрицы (например, см. Nx). Однако стандартная библиотека Elixir не использовала диапазоны с шагом в своих API. Elixir v1.14 начинает использовать шаги с поддержкой диапазонов с шагом в нескольких функциях. Одной из них является Enum.slice/2:
letters = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j"] Enum.slice(letters, 0..5//2) #=> ["a", "c", "e"]
binary_slice/2 (и binary_slice/3 для полноты) была добавлена в модуль Kernel, которая работает с байтами и также поддерживает диапазоны с шагом:
binary_slice("Elixir", 1..5//2)
#=> "lxr"
Инспекция на основе выражений и улучшения Inspect
В Elixir принято реализовывать протокол Inspect для неявных структур, чтобы их инспектировали со специальной нотацией, напоминающей это:
MapSet.new([:apple, :banana]) #MapSet<[:apple, :banana]>
Это обычно делается, когда содержимое структуры или часть его является частной, и представление %name{...} раскрыло бы поля, которые не входят в общедоступный API.
Недостатком соглашения #name<...> является то, что инспектируемый вывод не является допустимым кодом Elixir. Например, вы не можете скопировать инспектируемый вывод и вставить его в сеанс IEx.
Elixir v1.14 изменяет соглашение для некоторых структур стандартной библиотеки. Реализация Inspect для этих структур теперь возвращает строку с допустимым выражением Elixir, которое воспроизводит структуру при её оценке. В примере MapSet выше это выглядит так:
fruits = MapSet.new([:apple, :banana]) MapSet.put(fruits, :pear) #=> MapSet.new([:apple, :banana, :pear])
Выражение MapSet.new/1 оценивается точно так же, как и структура, которую мы инспектируем. Это позволяет нам скрыть внутренние детали MapSet, сохраняя при этом его как допустимый код Elixir. Эта инспекция на основе выражений была реализована для Version.Requirement, MapSet и Date.Range.
Наконец, мы улучшили протокол Inspect для структур, чтобы поля инспектировались в порядке их объявления в defstruct. Опция :optional также была добавлена при выводе протокола Inspect, что даёт разработчикам больше контроля над представлением структуры. Смотрите обновлённую документацию для Inspect для общего обзора доступных подходов и опций.
v1.14.1 (2022-10-10)
1. Улучшения
Elixir
- [Kernel] Частичная обработка литералов в атрибутах модуля
- [Kernel] Не добавлять зависимости от времени компиляции для литералов в качестве значений по умолчанию в
Application.compile_env/3внутри атрибутов модулей - [Macro] Добавить
Macro.expand_literals/2иMacro.expand_literals/3 - [System] Добавить
:close_stdinвSystem.shell/2
Mix
- [mix test] Поддержка опции
--all-warnings
2. Исправления ошибок
Elixir
- [Kernel] Исправление вводящего в заблуждение предупреждения, когда
:uniqуказано вfor-компрессиях, а результат не используется - [Kernel] Улучшение сообщения об ошибке, когда есть конфликтующая структура и игнорирование конфликта модулей
- [Kernel] Не удалять атрибут
@enforce_keysпосле объявленияdefstruct - [Kernel] Не завершать проверку модулей с отсутствующей частью
:debug_info - [Macro] Исправление ошибки в
Macro.to_string/2при преобразовании AST с:erlang.binary_to_atom/2 - [String] Исправление
String.split/3иString.next_grapheme/1, которые возвращали неверные результаты при неверном кодировании UTF-8 - [System] Не закрывать stdin по умолчанию в
System.shell/2 - [URI] Не возвращать
uri.portкак:undefinedв определённых случаях вURI.new/1
ExUnit
- [ExUnit.DocTest] Не выводить ошибку при указании как
:moduledoc, так и функций в:only
IEx
- [CLI] Исправление обработки неверных аргументов, когда указан
--no-pry
Mix
- [mix format] Не кэшировать входные данные из
.formatter.exs, чтобы они правильно переоценивались при каждом вызове
v1.14.0 (2022-09-01)
1. Улучшения
EEx
- [EEx] Поддержка многострочных комментариев в EEx с помощью
<%!-- --%> - [EEx] Добавление
EEx.tokenize/2
Elixir
- [Access] Добавить
Access.slice/1 - [Application] Добавить
Application.compile_env/4иApplication.compile_env!/3для чтения среды компиляции внутри макросов - [Calendar] Поддержка разбора базового формата ISO8601 с
DateTime.from_iso8601/2 - [Calendar] Добавить
day/hour/minuteвadd/diffчерез разные модули календаря - [Code] Добавить
:normalize_bitstring_modifiersвCode.format_string!/2 - [Code] Выводить предупреждения о устаревании и типе для недопустимых опций в
Code.compile_string/2иCode.compile_quoted/2 - [Code] Выводить предупреждение, если используется устаревленный лексический трекер в eval
- [Code] Добавить
Code.env_for_eval/1иCode.eval_quoted_with_env/3 - [Code] Улучшить трассировки стека из операций eval для Erlang/OTP 25+
- [Code.Fragment] Добавить поддержку
__MODULE__в нескольких функциях - [Code.Fragment] Поддержка предложений окружения и контекста через несколько строк
- [Enum] Разрешить срез с шагом в
Enum.slice/2 - [File] Поддержка
dereference_symlinks: trueвFile.cp/3иFile.cp_r/3 - [Float] Не отображать числа с плавающей точкой в экспоненциальной записи, если они меньше
1.0e16и дробная часть точно равна нулю - [Float] Добавить
Float.min_finite/0иFloat.max_finite/0 - [Inspect] Улучшить вывод сообщений об ошибках, когда существует некорректная реализация протокола
Inspect - [Inspect] Разрешить
:optionalпри выводе протокола Inspect для скрытия полей, которые совпадают со своим значением по умолчанию - [Inspect] Выводить поля структуры в порядке их объявления в
defstruct - [Inspect] Использовать инспекцию на основе выражений для
Date.Range,MapSetиVersion.Requirement - [IO] Поддержка
Macro.Envи ключевых слов как определений трассировки стека вIO.warn/2 - [IO] Добавить
IO.ANSI.syntax_colors/0и связанную конфигурацию, которая будет использоваться в IEx иdbg - [Kernel] Добавить новый
dbg/0-2макрос - [Kernel] Разрешить любое выражение-ограничение как размер битовой строки в совпадении с образцом
- [Kernel] Разрешить составные типы с фиксаторами как ключ карты в совпадении с образцом
- [Kernel] Выводить экранированный символ управления, когда он появляется как неожиданный токен
- [Kernel] Выводить предупреждение о похожих идентификаторах, не являющихся ASCII
- [Kernel] Добавить
..как нульарный оператор, возвращающий0..-1//1 - [Kernel] Реализовать рекомендации Unicode Technical Standard #39. В частности, мы выводим предупреждение о смежных сценариях и ограничиваем идентификаторы до одиночных сценариев или строго ограниченных смешанных сценариев
- [Kernel] Автоматически выполнять преобразование NFC идентификаторов
- [Kernel] Добавить
binary_slice/2иbinary_slice/3 - [Kernel] Лениво расширять атрибуты модулей, чтобы избежать зависимостей во время компиляции
- [Kernel] Автоматически каскадировать
generated: trueаннотации при расширении макроса - [Keyword] Добавить
Keyword.from_keys/2иKeyword.replace_lazy/3 - [List] Добавить
List.keysort/3с поддержкой функцииsorter - [Macro] Добавить
Macro.classify_atom/1иMacro.inspect_atom/2 - [Macro] Добавить
Macro.path/2 - [Macro.Env] Добавить
Macro.Env.prune_compile_info/1 - [Map] Добавить
Map.from_keys/2иMap.replace_lazy/3 - [MapSet] Добавить
MapSet.filter/2,MapSet.reject/2иMapSet.symmetric_difference/2 - [Node] Добавить
Node.spawn_monitor/2иNode.spawn_monitor/4 - [Module] Поддержка нового атрибута
@after_verifyдля выполнения кода при проверке модуля - [PartitionSupervisor] Добавить
PartitionSupervisor, который запускает несколько изолированных разделов одного и того же дочернего элемента для масштабируемости - [Path] Добавить
Path.safe_relative/1иPath.safe_relative_to/2 - [Registry] Добавить
Registry.count_select/2 - [Stream] Добавить
Stream.duplicate/2иStream.transform/5 - [String] Поддержка пустых списков поиска в
String.replace/3,String.split/3иString.splitter/3 - [String] Разрешить срез с шагом в
String.slice/2 - [Task] Добавить опцию
:zip_input_on_exitвTask.async_stream/3 - [Task] Сохранить
:mfaв структуреTaskдля целей рефлексии - [URI] Добавить
URI.append_query/2 - [Version] Добавить
Version.to_string/1 - [Version] Окрашивание
Version.Requirementисходного кода в протоколеInspect
ExUnit
- [ExUnit] Добавить
ExUnit.Callbacks.start_link_supervised!/2 - [ExUnit] Добавить
ExUnit.run/1для повторного выполнения модулей тестов - [ExUnit] Окрашивание сводки в жёлтый цвет с сообщением, когда все тесты исключены
- [ExUnit] Отображение понятной ошибки, когда имя теста слишком длинное
IEx
- [IEx] Оценивание
--dot-iexстроки за строкой - [IEx] Добавить построчное оценивание IEx точек останова
- [IEx.Autocomplete] Автодополнение модификаторов битовой строки (после
::внутри<<...>>) - [IEx.Helpers] Разрешить указание атома для
pid/1 - [IEx.Helpers] Поддержка сигилов в
h/1
Logger
- [Logger] Добавить
Logger.put_process_level/2
Mix
- [Mix] Добавить
:config_pathи:lockfileопции кMix.install/2 - [mix compile] Добавить
--no-optional-depsдля пропуска необязательных зависимостей, чтобы проверить, что компиляция работает без необязательных зависимостей - [mix compile] Включать информацию о столбце в диагностике ошибок, когда это возможно
- [mix deps]
Mix.Dep.Convergerтеперь указывает, какие зависимости образовали цикл - [mix do] Поддержка
--appопции для ограничения рекурсивных задач в проектах типа «зонтик» - [mix do] Разрешить использование
+в качестве разделителя задач вместо запятой - [mix format] Поддержка имени файла в
mix format -при чтении из стандартного ввода - [mix format] Компилировать, если отсутствуют плагины
mix format - [mix new] Не разрешать создание проектов с именами приложений, которые конфликтуют с параметрами командной строки многоаргументной виртуальной машины Erlang
- [mix profile] Возвращать возвращаемое значение профилируемой функции
- [mix release] Сделать сжатие BEAM опциональным
- [mix release] Позволить
:runtime_config_pathприниматьfalseдля пропускаconfig/runtime.exs - [mix test] Улучшить сообщение об ошибке, когда пакет тестов завершается неудачно из-за покрытия кода
- [mix test] Поддержка
:test_elixirc_optionsи по умолчанию не генерировать документацию и фрагмент отладочной информации для тестов - [mix xref] Поддержка флага
--groupвmix xref graph
2. Исправления ошибок
Elixir
- [Calendar] Обрабатывать ширины с "0" в них в
Calendar.strftime/3 - [CLI] Улучшить сообщения об ошибках при неправильном использовании
--rpc-eval - [CLI] Возвращать правильный код выхода в Windows
- [Code] Не выводить предупреждения при форматировании кода
- [Enum] Разрешить срезам превышать начальную и конечную позиции
- [Kernel] Не разрешать ограниченные символы в идентификаторах в соответствии с UTS39
- [Kernel] Определить поле
__exception__какtrueпри расширении исключений в типовредставлениях - [Kernel] Выводить предупреждение, если любой из псевдонимов
True,False, иNilиспользуется - [Kernel] Выводить предупреждение об атрибутах
@deriveбез вывода - [Kernel] Удалить зависимость во время компиляции от
defimpl :for - [Kernel] Отслеживать все арности импортированных функций
- [Kernel] Исправить равенство в ограничениях для динамических диапазонов без шагов
- [Module] Исправить цикл при объединении переменных типов
- [Protocol] Выводить предупреждение, если у протокола нет определений
- [Regex] Отображать варианты списка при инспектировании вручную определённого Regex с
Regex.compile/2 - [String] Разрешить срезам превышать начальную и конечную позиции
- [System] Вызывать негенерическое исключение при отсутствии переменной окружения в
System.fetch_env!/1для соответствия операциям с картами
ExUnit
- [ExUnit] Не завершать работу при сравнении неизвестных связей в ограничениях
- [ExUnit] Правильно отображать различия при сравнении некорректных списков со строками в хвостовой позиции
- [ExUnit] Добавить короткий хеш в
tmp_dirв ExUnit, чтобы избежать коллизий имён тестов - [ExUnit] Не сохранять логи в форматировщике командной строки (это снижает потребление памяти для пакетов тестов с
capture_log) - [ExUnit] Выполнять обратный вызов
ExUnit.after_suite/1даже при отсутствии запущенных тестов - [ExUnit] Исправить сценарий, где
setupс импортированной функцией изнутриdescribeне удалось скомпилировать
IEx
- [IEx] Запретить краткий оператор конвейера после соответствий
- [IEx] Исправить
exports/1в IEx для длинных имён функций
Mix
- [mix compile.elixir] Исправить
--warnings-as-errorsпри использовании с--all-warnings - [mix compile.elixir] Обеспечить каскадирование семантической перекомпиляции к зависимым путям
- [mix compile.elixir] Заблокировать компилятор, чтобы избежать одновременного использования
- [mix format] Не добавлять новые строки, если отформатированный файл пустой
- [mix format] Правильно скомпилировать зависимости от
mix format - [mix release] Установить
RELEASE_MODEтолько после выполненияenv.{sh,bat} - [mix release] Разрешить каскадирование конфигурации режима приложения к зависимостям
- [mix xref] Не выводить уже сконсолидированные предупреждения во время
mix xref trace - [Mix] Не запускать приложения с
runtime: falseприMix.install/2
3. Мягкие устаревания (предупреждения не выведены)
Elixir
- [File] Передача обратного вызова в качестве третьего аргумента в
File.cp/3иFile.cp_r/3устарело. Вместо этого передайте обратный вызов в качестве значения ключа:on_conflictв списке ключевых слов
EEx
- [EEx] Использование
<%# ... %>для комментариев устарело. Пожалуйста, используйте<% # ... %>или новые многострочные комментарии с<%!-- ... --%>
Logger
- [Logger] Устарели
Logger.enable/1иLogger.disable/1в пользуLogger.put_process_level/2
Mix
- [mix cmd] Опция
--appвmix cmd CMDустарела в пользу более эффективнойmix do --app app cmd CMD
4. Жесткие устаревания
Elixir
- [Application] Вызов
Application.get_env/3и аналогичных функций в теле модуля теперь не рекомендуется, используйтеApplication.compile_env/3вместо этого - [Bitwise]
use Bitwiseустарело, используйтеimport Bitwiseвместо этого - [Bitwise]
~~~устарело в пользуbnotдля ясности - [Kernel.ParallelCompiler] Возвращение списка или двухэлементной кортежи из
:each_cycleустарело, вместо этого возвращайте кортеж{:compile | :runtime, modules, warnings} - [Kernel] Устарел оператор
<|>для избежания неоднозначности с предстоящими расширенными числовыми операторами - [String] Устарело передача бинарного скомпилированного шаблона в
String.starts_with?/2
Logger
- [Logger] Устарело использование
$levelpadдля форматирования сообщений
Mix
- [Mix]
Mix.Tasks.Xref.calls/1устарело в пользу трейсеров компиляции
5. Изменения, несовместимые с предыдущими версиями
Mix
- [mix local.rebar] Удалена поддержка rebar2, которая не обновлялась 5 лет и больше не поддерживается в последних версиях Erlang/OTP
v1.13
Список изменений для релизов v1.13 можно найти в ветке v1.13.
© 2012 Plataformatec
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.14.1/changelog.html