Spec-Zone.ru › Elixir 1.18

Исходный код Изменения для Elixir v1.18

Elixir v1.18 — это впечатляющее обновление с улучшениями в двух основных областях экосистемы Elixir: теоретико-множественные типы и серверы языков. Оно также включает встроенную поддержку JSON и добавляет новые возможности в библиотеку модульных тестов. Вот краткий обзор.

Улучшения системы типов

Самым захватывающим изменением в Elixir v1.18 является проверка типов вызовов функций, наряду с постепенным выводом типов для шаблонов и возвращаемых значений. Чтобы понять, как это повлияет на ваши программы, рассмотрите следующий код в файле "lib/user.ex":

defmodule User do
  defstruct [:age, :car_choice]

  def drive(%User{age: age, car_choice: car}, car_choices) when age >= 18 do
    if car in car_choices do
      {:ok, car}
    else
      {:error, :no_choice}
    end
  end

  def drive(%User{}, _car_choices) do
    {:error, :not_allowed}
  end
end

Система типов Elixir выведет, что функция drive/2 ожидает структуру %User{} и возвращает либо {:ok, dynamic()}, либо {:error, :no_choice}, либо {:error, :not_allowed}.

Поэтому следующий код в отдельном модуле (в отдельном или в том же файле) должен выдать нарушение из-за неверного аргумента:

User.drive({:ok, %User{}}, car_choices)

Вот предупреждение:

    warning: incompatible types given to User.drive/2:

        User.drive({:ok, %User{age: nil, car_choice: nil}}, car_choices)

    given types:

        {:ok, %User{age: nil, car_choice: nil}}, empty_list()

    but expected one of:

        dynamic(%User{age: term(), car_choice: term()}), dynamic()

    where "car_choices" was given the type:

        # type: empty_list()
        # from: lib/foo.ex:21:17
        car_choices = []

    typing violation found at:
    │
 22 │     User.drive({:ok, %User{}}, car_choices)
    │          ~
    │
    └─ lib/foo.ex:22:10: Example.run/0

Несоответствующие аргументы отображаются красным цветом, если ваш терминал поддерживает цвет ANSI.

И следующий фрагмент выведет предупреждение, потому что ветвь :error никогда не будет соответствовать, так как это не допустимый тип возвращаемого значения для вызова User.drive/2.

case User.drive(user, car_choices) do
  {:ok, car} -> car
  :error -> Logger.error("User cannot drive")
end

И вот предупреждение:

    warning: the following clause will never match:

        :error

    because it attempts to match on the result of:

        User.drive(user, car_choices)

    which has type:

        dynamic({:ok, term()} or {:error, :no_choice} or {:error, :not_allowed})

    typing violation found at:
    │
 26 │       :error -> Logger.error("User cannot drive")
    │       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    │
    └─ lib/foo.ex:26: Example.run/0

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

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

  • Вывод типов для шаблонов (вывод типов для охранных условий будет частью будущей версии)

  • Проверка типов всех конструкций языка, включая локальные и удаленные вызовы, за исключением for, with, и замыканий

  • Проверка типов всех функций, встраиваемых компилятором, указанных в Kernel

  • Проверка типов всех функций преобразования, встраиваемых компилятором

  • Поддержка кортежей и списков в качестве составных типов, а также проверка типов их основных операций

  • Обнаружение ветвей и шаблонов, которые никогда не будут соответствовать, начиная с case, cond, и =

  • Обнаружение неиспользуемых ветвей в закрытых функциях

Улучшения ExUnit

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

Например, Elixir поставляется с локальным, децентрализованным и масштабируемым хранилищем ключей-значений под названием Registry. Реестр может быть разделен, и его реализация различается в зависимости от того, включено ли разделение. Следовательно, при тестировании необходимо убедиться, что оба режима используются. С помощью Elixir v1.18 мы можем добиться этого, написав:

defmodule Registry.Test do
  use ExUnit.Case,
    async: true,
    parameterize: [
      %{partitions: 1},
      %{partitions: 8}
    ]

  # ... the actual tests ...
end

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

ExUnit также позволяет указать группы тестов. Хотя ExUnit поддерживает одновременный запуск тестов, эти тесты не должны иметь общего состояния между собой. Однако в крупных приложениях может быть распространённым явлением, когда некоторые тесты зависят от общего состояния, а другие зависят от совершенно отдельного состояния. Например, часть ваших тестов может зависеть от Cassandra, а другие — от Redis. До Elixir v1.18 эти тесты не могли выполняться параллельно, но в v1.18 они могут, при условии, что они назначены в разные группы. Тестовые модули в одной группе не выполняются параллельно, но между группами они могут.

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

mix format --migrate

Команда mix format теперь поддерживает явный флаг --migrate , который преобразует устаревшие конструкции Elixir в их самые последние версии. Поскольку этот флаг переписывает абстрактное синтаксическое дерево (AST), нет гарантии, что преобразованный формат всегда будет допустимым при использовании совместно с макросами, которые также выполняют переписывание AST.

В этом релизе выполняются следующие миграции:

  • Нормализация скобок в модификаторах битовых строк — удаляет ненужные скобки в известных модификаторах битовых строк, например, <<foo::binary()>> становится <<foo::binary>>, или добавляет скобки для пользовательских модификаторов, где <<foo::custom_type>> становится <<foo::custom_type()>>.

  • Список символов как сигилы — форматирует списки символов как сигилы ~c, например 'foo' становится ~c"foo".

  • unless как отрицательные if — переписывает выражения unless с использованием if с отрицательным условием, например, unless foo do становится if !foo do.

В будущих версиях могут быть добавлены дополнительные миграции.

Поддержка JSON

В этом релизе включена официальная поддержка кодирования и декодирования JSON.

Как кодер, так и декодер полностью соответствуют стандартам RFC 8259 и ECMA 404.

Кодирование

Кодирование можно выполнить с помощью функций JSON.encode!/1 и JSON.encode_to_iodata!/1. Правила кодирования по умолчанию применяются следующим образом:

Elixir JSON
integer() | float() Число
true | false Булево значение
nil Null
binary() Строка
atom() Строка
list() Массив
%{binary() => _} Объект
%{atom() => _} Объект
%{integer() => _} Объект

Вы также можете реализовать протокол JSON.Encoder для пользовательских структур данных. Elixir уже реализует протокол для всех типов календаря.

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

  @derive {JSON.Encoder, only: [...]}
  defstruct ...

Декодирование

Декодирование можно выполнить с помощью функций JSON.decode/2 и JSON.decode!/2. Правила декодирования по умолчанию применяются следующим образом:

JSON Elixir
Число integer() | float()
Булево значение true | false
Null nil
Строка binary()
Объект %{binary() => _}

Слушатели сервера языка

4 месяца назад мы приветствовали официальную команду серверов языка с целью объединения усилий по разработке кодовой интуиции, инструментов и редакторов в Elixir. Elixir v1.18 добавляет новые функции в этом направлении, введя блокировки и слушателей для компиляции. Давайте разберемся, что это значит.

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

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

Эти улучшения улучшают не только инструменты редактирования, но также напрямую приносят пользу таким проектам, как IEx и Phoenix. Например, вы можете вызвать IEx.configure(auto_reload: true) , и IEx автоматически перезагрузит модули, изменённые где-либо ещё, как в другом терминале, так и в вашем IDE.

Возможные несовместимости

Этот релиз больше не поддерживает WERL (графический интерфейс пользователя в Windows, используемый Erlang 25 и ранее). Для лучшего пользовательского опыта в терминалах Windows используйте Erlang/OTP 26+ (это также последний релиз Elixir, поддерживающий Erlang/OTP 25).

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

def foo(x = {:ok, y}, x = y)

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

def foo(x = y, y = z, z = x)

Хотя определение выше могло бы быть успешным (при условии, что все три аргумента равны), цикл не нужен и может быть удалён, как показано ниже:

def foo(x = y, y = z, z)

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

def foo(x, y, z) when x == y and y == z

v1.18.1 (2024-12-24)

1. Улучшения

  • [Kernel] Не генерировать предупреждения о нарушении типов при сравнении или сопоставлении с литералами
  • [Kernel] Не валидировать пункты частных переопределяемых функций

2. Исправления ошибок

Elixir

  • [Code.Fragment] Убедиться, что Code.Fragment.container_cursor_to_quoted/2 с :trailing_fragment парсит выражения, которые поддерживались в предыдущих версиях
  • [Kernel] Не аварийно завершаться при обнаружении нарушения типа при динамическом диспетчеризации
  • [Kernel] Правильно аннотировать исходный код предупреждений, выдаваемых компилятором, с помощью аннотации @file
  • [Kernel] Правильно аннотировать исходный код предупреждений, выдаваемых системой типов, с помощью аннотации @file
  • [Kernel] Удалить :no_parens метаданные при использовании захвата с арностью во всех случаях
  • [Kernel] Убедиться, что диагностические следы остаются обратной совместимыми

ExUnit

  • [ExUnit.Case] Убедиться, что асинхронные группы не выполняются одновременно, пока набор тестов еще загружается
  • [ExUnit.Case] Убедиться, что --repeat-until-failure может быть комбинирован с группами

Mix

  • [mix compile.elixir] Сохранить результаты компиляции, если компиляция завершится неудачно из-за --warnings-as-errors
  • [mix deps.loadpaths] Добавить блокировку сборки
  • [mix escript.build] Убедиться, что сборка выполняется успешно, когда консолидация протоколов отключена
  • [Mix.Shell] Убедиться, что кодировка правильно обрабатывается в системах Windows и Unix

v1.18.0 (2024-12-19)

1. Улучшения

Elixir

  • [CLI] Добавить экспериментальные скрипты PowerShell для elixir, elixirc, и mix в Windows. Они обеспечивают более безопасную точку входа для запуска Elixir из других платформ
  • [Calendar] Добавить Duration.to_string/1
  • [Code] Поддержка нескольких вариантов миграции в Code.format_string!/2
  • [Code] Добавить скобки вокруг -- и --- в Code.format_string!/2, чтобы сделать приоритет более ясным
  • [Code] Включить больше метаданных в Code.string_to_quoted/2 при token_metadata: true, чтобы помочь вычислить диапазоны из AST
  • [Code.Fragment] Иметь :capture_arg как свою собственную запись в Code.Fragment.surround_context/2
  • [Config] Добавить Config.read_config/1
  • [Enumerable] Добавить Enum.product_by/2 и Enum.sum_by/2
  • [Exception] Добавить исключение MissingApplicationsError для обозначения отсутствующих приложений
  • [JSON] Добавить новый модуль JSON с функциями кодирования и декодирования
  • [JSON] Реализовать JSON.Encoder для всех типов Calendar
  • [Kernel] Обновить парсинг исходного кода для соответствия последним рекомендациям UTS #55. В частности, смешанные сценарии разрешены в идентификаторах, если они разделены подчеркиваниями (_), например, http_сервер. Предыдущие разрешенные идентификаторы были очень строгими, которые смешивали латинские и другие сценарии, такие как японское слово для футболки, Tシャツ, теперь также требуют подчеркивания.
  • [Kernel] Выдавать предупреждения о двусмысленности в идентификаторах
  • [Kernel] Проверить тип двоичных генераторов
  • [Kernel] Отслеживать тип кортежей в шаблонах и внутри elem/2
  • [Kernel] Выполнять валидацию корневых узлов AST в unquote и unquote_splicing для обнаружения ошибок раньше
  • [Kernel] Добавить информацию о исходном коде, поведении и записях в метаданные фрагментов Docs
  • [Kernel] Поддержка детерминированных сборок в тандеме с Erlang, установив ERL_COMPILER_OPTIONS=deterministic. Имейте в виду, что детерминированные сборки удаляют исходный код и другую информацию времени компиляции, которая может быть важной для программ
  • [Kernel] Разрешить условную активацию псевдонимов и импортов в теле модуля
  • [List] Добавить List.ends_with?/2
  • [Macro] Улучшить обработку dbg if/2, with/1 и блоков кода
  • [Macro] Добавить Macro.struct_info!/2 для возврата информации о структуре, отражающей mod.__info__(:struct)
  • [Registry] Добавить Registry.lock/3 для локальной блокировки
  • [PartitionSupervisor] Добавить PartitionSupervisor.resize!/2 для изменения количества разделов в диспетчере (до предела, с которым он был запущен)
  • [Process] Обрабатывать произвольно большие целочисленные значения в Process.sleep/1
  • [Protocol] Добавить @undefined_impl_description для настройки сообщения об ошибке, когда реализация не определена
  • [Protocol] Добавить __deriving__/1 как необязательную макро-обработчик вызова Protocol, больше не требующую пустых реализаций
  • [String] Просмотреть специальные пробелы и символы нулевой ширины с использованием их представления Юникод
  • [String] Обновить Юникод до 16.0

ExUnit

  • [ExUnit] Поддержка параметризованных тестов на ExUnit.Case
  • [ExUnit] Поддержка групп тестов: тесты в одной группе никогда не выполняются одновременно
  • [ExUnit.Case] Добавить test_pid в качестве тега

IEx

  • [IEx] Добавить IEx.configure(auto_reload: true) для автоматического получения перекомпилированных модулей из процессов других операционных систем
  • [IEx] Добавить поддержку :dot_iex для IEx.configure/1
  • [IEx] Добавить отчет о нормальных/завершающих выходах в IEx

Mix

  • [mix compile] Убедиться, что только один процесс операционной системы может компилировать в данный момент
  • [mix deps.get] Убедиться, что только один процесс операционной системы может получать зависимости в данный момент
  • [mix format] Добавить mix format --migrate для миграции от устаревшей функциональности
  • [mix format] Добавить новые параметры и метаданные для улучшения форматирования, применяемого редакторами и другими средами
  • [mix test] Отметить провал макета, если тесты требуют или компилируются неудачно
  • [Mix.Project] Добавить конфигурацию :listeners для прослушивания событий компиляции от текущих и других процессов операционных систем
  • [Mix.Task.Compiler] Добавить API для получения всех сохраненных диагностических данных компилятора
  • [Mix.Task.Compiler] Добавить API для получения всех задач компилятора

2. Исправления ошибок

Elixir

  • [Code] Исправить метаданные разделителей для атомов в одинарных кавычках и удалённых вызовов в Code.string_to_quoted/2
  • [Code.Formatter] Исправить форматирование, добавляющее дополнительные экранирования в цитированные удалённые вызовы
  • [Code.Fragment] Правильно обработать ключи ключевых слов как свои собственные записи
  • [Inspect.Algebra] Убедиться, что next_break_fits учитывает line_length
  • [Kernel] Проверить AST в unquote и unquote_splicing для обеспечения лучших сообщений об ошибках вместо слишком позднего сбоя внутри компилятора
  • [Kernel] Избежать сбоев при выводе диагностики в коде, использующем \t для отступов
  • [Module] Включить строку и имя атрибута модуля при отслеживании его псевдонимов
  • [Stream] Не останавливать потоки дважды в Stream.transform/5
  • [URI] Исправить ошибку при передаче URI без схемы в URI.merge/2

ExUnit

  • [ExUnit.Assertions] Выдать ошибку, если в assert/1 используются защитные условия с =
  • [ExUnit.Assertions] Отформатировать вставленные/удаленные карты в утверждениях списка

IEx

  • [IEx.Helpers] IEx.Helpers.recompile/0 будет перезагружать модули, изменённые другими процессами операционной системы

Mix

  • [mix compile] Убедиться, что предупреждения из внешних ресурсов выводятся с помощью --all-warnings при отсутствии изменений файлов
  • [mix deps.compile] Исправить проблемы с экранированием при вызове rebar3 в некоторых случаях
  • [mix escript] Исправить макет escript и поддержку хранения каталогов priv
  • [mix release] Сделать файлы .app детерминированными в релизах
  • [Mix.Shell] Исправить Mix.Shell в Windows при выводе символов не в формате UTF-8

3. Мягкие устаревания (предупреждения не выводятся)

Elixir

  • [Inspect.Algebra] color/3 устарело в пользу color_doc/3
  • [Inspect.Algebra] fold_doc/2 устарело в пользу fold/2
  • [Kernel] Устарело unless в пользу if. Используйте mix format --migrate для автоматизации миграции
  • [Macro] Macro.struct!/2 устарело в пользу Macro.struct_info!/2
  • [Protocol] Определение __deriving__/3 внутри реализации Any устарело, выведите его внутри самого определения протокола

4. Жёсткие устаревания

EEx

  • [EEx] <%# устарело в пользу <%!-- или <% #
  • [EEx] c:EEx.handle_text/2 устарело в пользу c:EEx.handle_text/3

Elixir

  • [Code] Настройка :warnings_as_errors устарела через Code.put_compiler_option/2. Это не должно повлиять на разработчиков, так как опция :warnings_as_errors управляется задачами Mix, а не используется напрямую через модуль Code
  • [Enumerable] Устарел возврат функции с двумя аргументами в Enumerable.slice/1
  • [List] List.zip/1 устарел в пользу Enum.zip/1
  • [Module] Устарел Module.eval_quoted/3 в пользу Code.eval_quoted/3
  • [Range] Устарел вывод отрицательных диапазонов в Range.new/2
  • [Tuple] Tuple.append/2 устарел, используйте Tuple.insert_at/3 вместо него

Mix

  • [mix cmd] Устарел mix cmd --app APP в пользу mix do --app APP
  • [mix compile] Настройка :warnings_as_errors в :elixirc_options устарела. Вместо этого передайте флаг --warnings-as-errors в mix compile. В качестве альтернативы, вы можете алиасить задачу: aliases: [compile: "compile --warnings-as-errors"]
  • [mix test] Настройка :warnings_as_errors в :test_elixirc_options устарела. Вместо этого передайте флаг --warnings-as-errors в mix test. В качестве альтернативы, вы можете алиасить задачу: aliases: [test: "test --warnings-as-errors"]
  • [Mix.Tasks.Compile] Устарел compilers/0 в пользу Mix.Task.Compiler.compilers/0

v1.17

Список изменений для выпусков v1.17 можно найти в ветке v1.17.

← Предыдущая страница Справочник API
Следующая страница → Введение

Скачать версию ePub

Создано с помощью ExDoc (v0.36.1) для программного языка Elixir

© 2012-2024 The Elixir Team
Licensed under the Apache License, Version 2.0.
https://hexdocs.pm/elixir/1.18.1/changelog.html

Spec-Zone.ru

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