Elixir v1.13.0 released

Announcement: Elixir v1.13 released - The Elixir programming language

1. Enhancements

EEx

  • [EEx] Add :parser_options to EEx functions

Elixir

  • [Calendar] Add c:Calendar.year_of_era/3 to support calendars where the beginning of a new era does not align with the beginning of a new year
  • [CLI] Support --short-version on the CLI that does not boot the VM
  • [Code] Add Code.string_to_quoted_with_comments/2 and Code.quoted_to_algebra/2
  • [Code] Add more :token_metadata to aliases and remote calls when parsing strings
  • [Code] Add Code.Fragment module to provide best-effort information from code fragments. The module currently provides an updated Code.Fragment.cursor_context/2 with operator support and Code.Fragment.surround_context/2 which looks at a given position in a fragment and find its surrounding delimiters
  • [Code] Allow custom sigil formatting on Code.format_string!/2
  • [Code] Add {:on_module, bytecode, :none} trace to compilation tracers
  • [Enum] Optimize Enum.concat/1 for lists of lists
  • [Enum] Add Enum.slide/3
  • [Exception] Better format Elixir exceptions in Erlang
  • [Inspect] Allow default inspect fun to be set globally with Inspect.Opts.default_inspect_fun/1
  • [IO] Allow :eof to be given as limit to IO.getn/2
  • [Kernel] Support the :sigils option in import Mod, only: :sigils and allow the sigil modifiers to be also digits
  • [Kernel] Make get_in consistently abort when nil values are found
  • [Kernel] Improve compilation times by reducing the amount of copies of the AST across compiler processes
  • [Kernel] Raise if trying to define a module with a slash in its name
  • [Kernel] Warn when ?\ is used and there is no need for a escape character
  • [Kernel] Track structs in typespecs as export deps instead of compile-time deps
  • [Kernel] Add power operator (**/2)
  • [Keyword] Add Keyword.validate/2
  • [Keyword] Implement Keyword.filter/2 and Keyword.map/2
  • [List] Add List.keyfind!/3
  • [Macro] Add Macro.prewalker/1 and Macro.postwalker/1
  • [Macro.Env] Add the following reflection functions: required?/2, lookup_import/2, fetch_alias/2, and fetch_macro_alias/2
  • [Map] Implement Map.filter/2 and Map.map/2
  • [Module] Support :nillify_clauses in Module.get_definition/3
  • [Module] Add Module.attributes_in/1 and Module.overridables_in/1
  • [OptionParser] Add “did you mean?” suggestions to OptionParser.ParseError messages
  • [Record] Add record reflection via @__records__
  • [Task] Add Task.completed/1
  • [Task] Add Task.ignore/1 to keep a task running but ignoring all of its results
  • [Task] Reduce the amount of copying Task.async* functions
  • [URI] Add URI.new/1 and URI.new!/1

ExUnit

  • [ExUnit] Show hint if comparing different but equivalent strings
  • [ExUnit.CaptureIO] Add with_io/3 to return result with captured io
  • [ExUnit.CaptureLog] Add with_log/2 to return result with captured logs

IEx

  • [IEx.Autocomplete] Add path autocompletion whenever when the cursor follows "./ or "/ or "DRIVER: where DRIVER is a single letter
  • [IEx.Autocomplete] Add autocompletion for sigils, struct names, and struct fields
  • [IEx.Helpers] Allow multiple modules to be given to r/1

Logger

  • [Logger] Add Logger.put_application_level/2

Mix

  • [Mix] Add MIX_INSTALL_FORCE environment variable support
  • [Mix] Support :config and :system_env in Mix.install/2
  • [Mix] Add Mix.installed?/0
  • [Mix.Shell] Add :default option to Mix.Shell.yes?
  • [mix archive.install] Run loadconfig before building archive
  • [mix compile] Move Elixir version check to before deps are compiled, in order to give feedback earlier
  • [mix compile.elixir] Do not recompile files if their modification time change but their contents are still the same and the .beam files are still on disk
  • [mix compile.elixir] Do not recompile all Elixir sources when Erlang modules change, only dependent ones
  • [mix compile.elixir] Do not recompile Elixir files if mix.exs changes, instead recompile only files using Mix.Project or trigger a recompilation if a compiler option changes
  • [mix compile.elixir] Only recompile needed files when a dependency is added, updated or removed
  • [mix compile.elixir] Only recompile needed files when a dependency is configured
  • [mix deps] Add :subdir option to git deps
  • [mix escript.install] Run loadconfig before building escript
  • [mix format] Support :plugins in mix format that can hook into custom extensions and sigils
  • [mix format] Add Mix.Tasks.Format.formatter_for_file/2
  • [mix local.rebar] No longer support sub_dirs in Rebar 2 to help migration towards Rebar 3
  • [mix local.rebar] Support --if-missing option when installing Rebar
  • [mix local.rebar] Set REBAR_PROFILE=prod when compiling Rebar dependencies
  • [mix test] Support --profile-require=time to profile the time loading test files themselves
  • [mix test] Allow filtering modules from coverage using regex
  • [mix test] Allow the exit status of ExUnit to be configured and set the default to 2
  • [mix test] Exit with a status of 3 when coverage falls below threshold
  • [mix test] Write failed manifest when suite fails due to --warnings-as-errors
  • [mix test] Ignore MIX_TEST_PARTITION when partitions set to 1
  • [mix xref] Support multiple sinks and sources in mix xref graph
  • [mix xref] Add trace subcommand to print compilation dependencies between files
  • [mix xref] Add --fail-above option to mix xref
  • [mix xref] Add --label compile-connected to mix xref

2. Bug fixes

EEx

  • [EEx] Accept comments in EEx between do and the first clause
  • [EEx] Accept EEx expressions where -> is followed by newline

Elixir

  • [Application] Allow any expression as first argument of compile_env
  • [Application] Warn if Application.compile_env or Application.compile_env! are called without a require
  • [Code] Make sure :static_atoms_encoder in Code.string_to_quoted/2 also applies to quoted keyword keys
  • [Code] Ensure bindings with no context are returned as atoms instead of {binding, nil} in eval operations
  • [Inspect] Fix a bug when inspecting a non-binary bitstring with colors
  • [Kernel] Reject bidirectional formatting characters in strings and comments
  • [Kernel] Support escaping of terminators in uppercase sigils heredocs for consistency
  • [Kernel] Raise if __CALLER__ or __ENV__ or __STACKTRACE__ are used in match
  • [Kernel] Improve error message on invalid argument for byte_size from binary concat
  • [Kernel] Raise when aliasing non-Elixir modules without :as
  • [Kernel] Allow unquote_splicing inside %{...} without parens
  • [Kernel] Ensure that waiting on a struct expansion inside a typespec is correctly tracked as waiting time in the compiler
  • [Kernel] Correctly parse the atom . as a keyword list key
  • [Kernel] Do not leak variables from the first generator in with and for special forms
  • [Kernel] Fix column number on strings with NFD characters
  • [Kernel] Fix a bug where a combination of dynamic line in quote with unquote of remote calls would emit invalid AST metadata
  • [OptionParser] Validate switch types/modifiers early on to give more precise feedback
  • [Protocol] Add defdelegate to the list of unallowed macros inside protocols as protocols do not allow function definitions
  • [Protocol] Warn if @callback, @macrocallback and @optional_callbacks are defined inside protocol
  • [Protocol] Ensure protocol metadata is deterministic on consolidation
  • [Range] Always show step when range is descending
  • [String] Update Unicode database to version 14.0
  • [URI] Only percent decode if followed by hex digits (according to URL Standard)
  • [Version] Ensure proper precedence of and/or in version requirements

ExUnit

  • [ExUnit] Fix formatter and counters from ExUnit.run/0 to consider all tests in a module whenever if a module’s setup_all fails
  • [ExUnit] Allow doctests newlines to be terminated by CRLF

IEx

  • [IEx] Fix the loss of .iex.exs context after a pry session
  • [IEx] Stop evaluator before exiting IEx server to avoid evaluators leaking

Logger

  • [Logger] Raise clear error message for invalid :compile_time_purge_matching configuration
  • [Logger] Fix a bug where Logger would not reset its discard counter under some scenarios

Mix

  • [mix compile.elixir] Track transitive runtime dependencies coming from local/path dependencies
  • [mix compile.elixir] Recompile file if @external_resource is deleted
  • [mix compile.elixir] Print number of compiling files on all compiler cycles. This will make the Compiling N files (.ex) show up multiple times if necessary
  • [mix deps] Raise if local dep is unavailable while compiling
  • [mix deps.unlock] Fix blank output when unlocking a dependency that is not locked
  • [mix local.install] Do not respect MIX_DEPS_PATH for install commands
  • [mix release] Improve release scripts by making sure shell errors cascade (this is done by avoiding exporting and defining variables in a single step)
  • [mix release] Do not boot release if RELEASE_COOKIE is empty
  • [mix release] Allow releases running as a daemon to be restarted
  • [mix release] Raise proper error message when non-serializable values are in configs
  • [mix test] Fix coverage engine to also tag case, cond, and receive branches where the right side is a literal

3. Soft-deprecations (no warnings emitted)

Elixir

  • [Code] Environment options in Code.eval_quoted/3 and Code.eval_string/3, such as :aliases and :tracers, have been deprecated in favor of passing an environment
  • [IO] :all on IO.getn is deprecated in favor of :eof
  • [URI] URI.parse/1 is deprecated in favor of URI.new/1 and URI.new!/1

Mix

  • [mix format] Mix.Tasks.Format.formatter_opts_for_file/2 is deprecated in favor of Mix.Tasks.Format.formatter_for_file/2

4. Hard-deprecations

Elixir

  • [Code] Code.cursor_context/2 is deprecated, use Code.Fragment.cursor_context/2 instead
  • [Macro] Macro.to_string/2 is deprecated, use Macro.to_string/1 instead
  • [System] System.get_pid/0 is deprecated, use System.pid/0 instead
  • [Version] Using ! or != in version requirements is deprecated, use ~> or >= instead

Mix

  • [mix escript.build] :strip_beam option is deprecated in favor of :strip_beams
  • [Mix] :exit_code in Mix.raise/2 has been deprecated in favor of :exit_status
  • [Mix.Config] Mix.Config is deprecated in favor of Config module

Checksums

  • Precompiled.zip SHA1: 0f196f4de406882b5a3bcee7ac9e9b0c61ed1459
  • Precompiled.zip SHA512: 58ffe87d6eb89435d8605aee04556aa1a1ba25cf2fa7688d0a5da162d7d7c57b47b2f726b365a7aeb18832bf08de3db5d3ec0ed45e13da276438679f29e5e3ac
  • Docs.zip SHA1: c1aa5161ff3609d9fdda3a2e07f8cea9489622d2
  • Docs.zip SHA512: 248fa58bffcee933fef1aafc6a7a2d8ab71d2163f958e610eda0fc5c2af0184b50c2fa0de453bd2ee343eeeb0ed3e94543a1b665dc6960eb064af20be6ea9b11

Have fun!

68 Likes
  • [Kernel] Make get_in consistently abort when nil values are found
  • [Map] Implement Map.filter/2 and Map.map/2

:dancing_women: music to my ears!

8 Likes

A release with not that many replies so far; but I don’t think that is because we aren’t happy. There is just so much to praise :slight_smile:

I see some very interesting additions and fixes for sake of convenience, editors can become much smarter, better error messages etc etc.

Thanks a lot for this one!

Ps. It would be very convenient if this forum could link the functions like Hex docs :slight_smile:

4 Likes

Changelog for Elixir v1.13 — Elixir v1.13.0

4 Likes

I wonder why Task.ignore/3 requires OTP 24, and specifically the :erlang.monitor/3 function to be defined.

Probably to make use of the alias feature introduced in OTP 24. Otherwise when the task eventually finishes it’s response message would be stuck in the message queue unless there’s additional cleanup code.

3 Likes

Oh right. In the async function code:

      if function_exported?(:erlang, :monitor, 3) do
        ref = :erlang.monitor(:process, pid, alias: :demonitor)
        {ref, ref}
      else
        {owner, Process.monitor(pid)}
      end

So on OTP 23 there is no aliasing. The ignore function would still work but it would have no effect.

Ignore will raise on OTP23

Well I said that it “would” still work, if we removed the raise call, obviously :slight_smile:

1 Like

Like 90% of this release excites me. :007:

I am especially happy with the various Code module extensions since they bring Elixir more in line with other languages (and editor tooling) e.g. tree-sitter (parsing partial code).

That being said, Keyword.validate naming confuses me. I’d think validation implies “these fields must all be present or else it’s an error”. I know default values are supplied as well but that’s not strictly validation, that’s more like… Keyword.apply_defaults_and_validate, I suppose? I wouldn’t think it acceptable to pass an empty keyword list to a validate function and get an OK from it.

What am I missing here? :thinking:

1 Like

Agreed, validate is confusing as it only handles unknown keys but does not have concept of required keys. And in many cases I’ve been dealing with, extra keys can be just ignored. But not all. Do not try to give unknown keys to cowboy options in Phoenix :slight_smile:

This makes me so happy… :slight_smile:

Is it possible to configure this via compile time config? I tried things like:

import Config

config :logger, application_level: [
  my_wordy_app: :info
]

But it didn’t work.

Thank you!

Context here is important: keywords are often used to pass optional data, so not accepting an empty keyword list in Keyword.validate would actually make it unusable for the majority of use cases it was designed for!

And the reason why it doesn’t handle required keys is because the user can do a better job at that. Imagine that we also handled required keys in Keyword.validate. If a key is missing, the best we could do is say: “missing key :foo”. But a library author could actually do this:

opts[:foo] || raise ":foo key is missing. Please set it one of x, y, z, presenting this and that"

The only way to make Keyword.validate raise such a complete error message would be to build the whole validation machinery as found in NimbleOptions and other libraries, but that would then be out of scope for core IMO. If you need it, use NimbleOptions. :slight_smile:

And even then, there are other scenarios such as “this key is required only if this other key is given”, that can’t be expressed even with NimbleOptions at the moment. So TL;DR, it does a specific validation (keys must be within a certain set) and merge defaults along the way.

EDIT #32981: Even then, there are other validations beyond unknown keys and required keys, such as what is the type of each key.

5 Likes

Not really, I believe you would need to set those in your app initialization (the start/2 callback). You could have your start/2 callback read from config though and implement it as you want. :slight_smile:

1 Like

I see, you spotted a common use-case and enabled lower-friction mode for it in core. Got it. :+1:

As for the rest of your message, I was just about to say that if more complex validation is needed we can either (a) use NimbleOptions as you said, (b) reach for Ecto.Changeset (I’ve done this in the past and while it’s a bit verbose it does get the message [“don’t do this or that with those particular options”] across really well in teams), or (c) roll our own struct and have it raise or return :error tuples in its new function.

1 Like

My first core contribution to any language ever — and a very small one at that.

But I just wanted to say the experience was an absolute pleasure. The fact that a relatively inexperienced Elixir developer like me could make a suggestion, be enthusiastically encouraged to implement a PR, and get great feedback and support throughout the process was very encouraging.

Yet another reason to applaud Elixir in my book! :clap:

31 Likes