Elixir v1.18.0 released

Full announcement: Elixir v1.18 released: type checking of function calls, Language Server listeners, built-in JSON, and ExUnit improvements - The Elixir programming language

Release notes: Release v1.18.0 · elixir-lang/elixir · GitHub

Changelog

1. Enhancements

Elixir

  • [CLI] Add experimental PowerShell scripts for elixir, elixirc, and mix on Windows. Those provide a safer entry point for running Elixir from other platforms
  • [Calendar] Add Duration.to_string/1
  • [Code] Support several migration options in Code.format_string!/2
  • [Code] Add parenthesis around -- and --- in Code.format_string!/2 to make precedence clearer
  • [Code] Include more metadata in Code.string_to_quoted/2 when token_metadata: true to help compute ranges from the AST
  • [Code.Fragment] Have :capture_arg as its own entry in Code.Fragment.surround_context/2
  • [Config] Add Config.read_config/1
  • [Enumerable] Add Enum.product_by/2 and Enum.sum_by/2
  • [Exception] Add MissingApplicationsError exception to denote missing applications
  • [JSON] Add a new JSON module with encoding and decoding functionality
  • [JSON] Implement JSON.Encoder for all Calendar types
  • [Kernel] Update source code parsing to match UTS #55 latest recommendations. In particular, mixed script is allowed in identifiers as long as they are separate by underscores (_), such as http_сервер. Previously allowed highly restrictive identifiers, which mixed Latin and other scripts, such as the japanese word for t-shirt, Tシャツ, now require the underscore as well
  • [Kernel] Warn on bidirectional confusability in identifiers
  • [Kernel] Verify the type of the binary generators
  • [Kernel] Track the type of tuples in patterns and inside elem/2
  • [Kernel] Perform validation of root AST nodes in unquote and unquote_splicing to catch bugs earlier
  • [Kernel] Add source, behaviour, and record information to Docs chunk metadata
  • [Kernel] Support deterministic builds in tandem with Erlang by setting ERL_COMPILER_OPTIONS=deterministic. Keep in mind deterministic builds strip source and other compile time information, which may be relevant for programs
  • [Kernel] Allow aliases and imports to be enabled conditionally in module body
  • [List] Add List.ends_with?/2
  • [Macro] Improve dbg handling of if/2, with/1 and of code blocks
  • [Macro] Add Macro.struct_info!/2 to return struct information mirroring mod.__info__(:struct)
  • [Registry] Add Registry.lock/3 for local locking
  • [PartitionSupervisor] Add PartitionSupervisor.resize!/2 to resize the number of partitions in a supervisor (up to the limit it was started with)
  • [Process] Handle arbitrarily high integer values in Process.sleep/1
  • [Protocol] Add @undefined_impl_description to customize error message when an implementation is undefined
  • [Protocol] Add __deriving__/1 as optional macro callback to Protocol, no longer requiring empty implementations
  • [String] Inspect special whitespace and zero-width characters using their Unicode representation
  • [String] Update Unicode to 16.0

ExUnit

  • [ExUnit] Support parameterized tests on ExUnit.Case
  • [ExUnit] Support test groups: tests in the same group never run concurrently
  • [ExUnit.Case] Add test_pid as a tag

IEx

  • [IEx] Add IEx.configure(auto_reload: true) to automatically pick up modules recompiled from other operating system processes
  • [IEx] Add :dot_iex support to IEx.configure/1
  • [IEx] Add report for normal/shutdown exits in IEx

Mix

  • [mix compile] Ensure only a single operating system process can compile at a given time
  • [mix deps.get] Ensure only a single operating system process can fetch deps at a given time
  • [mix format] Add mix format --migrate to migrate from deprecated functionality
  • [mix format] Add new options and metadata to improve formatting applying by editors and other environments
  • [mix test] Taint failure manifest if requiring or compiling tests fail
  • [Mix.Project] Add a :listeners configuration to listen to compilation events from the current and other operating system processes
  • [Mix.Task.Compiler] Add API for fetching all persisted compiler diagnostics
  • [Mix.Task.Compiler] Add API for fetching all compiler tasks

2. Bug fixes

Elixir

  • [Code] Fix delimiter metadata for single quoted atoms and remote calls in Code.string_to_quoted/2
  • [Code.Formatter] Fix formatter adding extra escapes to quoted remote calls
  • [Code.Fragment] Properly handle keyword keys as their own entry
  • [Inspect.Algebra] Ensure next_break_fits respects line_length
  • [Kernel] Validate AST on unquote and unquote_splicing to provide better error reports instead of failing too late inside the compiler
  • [Kernel] Avoid crashes when emitting diagnostics on code using \t for indentation
  • [Module] Include module attribute line and name when tracing its aliases
  • [Stream] Do not halt streams twice in Stream.transform/5
  • [URI] Fix a bug when a schemaless URI is given to URI.merge/2

ExUnit

  • [ExUnit.Assertions] Raise if guards are used in assert/1 with =
  • [ExUnit.Assertions] Format inserted/deleted maps in list assertions

IEx

  • [IEx.Helpers] IEx.Helpers.recompile/0 will reload modules changed by other operating system processes

Mix

  • [mix compile] Ensure warnings from external resources are emitted with --all-warnings when files do not change
  • [mix deps.compile] Fix escaping issues when invoking rebar3 in some cases
  • [mix escript] Fix escript layout and support storing priv directories
  • [mix release] Make .app files deterministic in releases
  • [Mix.Shell] Fix Mix.Shell on Windows when outputting non UTF-8 characters

3. Soft deprecations (no warnings emitted)

Elixir

  • [Inspect.Algebra] color/3 is deprecated in favor of color_doc/3
  • [Inspect.Algebra] fold_doc/2 is deprecated in favor of fold/2
  • [Kernel] Deprecate unless in favor of if. Use mix format --migrate to automate the migration
  • [Macro] Macro.struct!/2 is deprecated in favor of Macro.struct_info!/2
  • [Protocol] Defining __deriving__/3 inside the Any implementation is deprecated, derive it inside the protocol definition itself

4. Hard deprecations

EEx

  • [EEx] <%# is deprecated in favor of <%!-- or <% #
  • [EEx] c:EEx.handle_text/2 is deprecated in favor of c:EEx.handle_text/3

Elixir

  • [Code] Setting :warnings_as_errors is deprecated via Code.put_compiler_option/2. This must not affect developers, as the :warnings_as_errors option is managed by Mix tasks, and not directly used via the Code module
  • [Enumerable] Deprecate returning a two-arity function in Enumerable.slice/1
  • [List] List.zip/1 is deprecated in favor of Enum.zip/1
  • [Module] Deprecate Module.eval_quoted/3 in favor of Code.eval_quoted/3
  • [Range] Deprecate inferring negative ranges on Range.new/2
  • [Tuple] Tuple.append/2 is deprecated, use Tuple.insert_at/3 instead

Mix

  • [mix cmd] Deprecate mix cmd --app APP in favor of mix do --app APP
  • [mix compile] :warnings_as_errors configuration in :elixirc_options is deprecated. Instead pass the --warnings-as-errors flag to mix compile. Alternatively, you might alias the task: aliases: [compile: "compile --warnings-as-errors"]
  • [mix test] :warnings_as_errors configuration in :test_elixirc_options is deprecated. Instead pass the --warnings-as-errors flag to mix test. Alternatively, you might alias the task: aliases: [test: "test --warnings-as-errors"]
  • [Mix.Tasks.Compile] Deprecate compilers/0 in favor of Mix.Task.Compiler.compilers/0

v1.17

The CHANGELOG for v1.17 releases can be found in the v1.17 branch.

96 Likes

Truly amazing work on the type system and the language in general. Thanks to everybody who contributed towards making this possible.

10 Likes

So happy about the type inference! I know it wasn’t so long ago that José was saying that it could possibly be too slow and not make it in—so cool it’s already here. Thanks all!

1 Like

:fire: :fire: :fire:

Small typo:

Prior to parameterized types, Elixir resorted on code generation, which increased compilation types

I believe both of those should be “times”, not “types”

EDIT: sorry lol. The first one should be “tests” and the second one should be “times”

2 Likes

After types release, all words starting with t shall be written as types.

48 Likes

It took me way longer to get this was a joke than I care to admit.

6 Likes

Thanks to José and everyone who made this possible. Can’t wait to explore the type system further.

Thanks for the release!

After trying it out, I’m getting some type warnings that imo should not be there. Here is an example:

  defmodule TestModule do
    def value(), do: true
  end

  def helper(val), do: if(val, do: TestModule, else: nil)

  def test() do
    case helper(true) do
      nil -> false
      module -> module.value()
    end
  end

At module.value(), I get this warning:

nil.value/0 is undefined (module nil is not available or is yet to be defined)

I think the type of module is dynamic(TestModule or nil), right? Except in the case branch, we can see that nil is not a possible value.

I use this pattern a lot so even though it’s only a warning, I’m getting a lot of them. This warning goes away if I convert the return value of helper into {:ok, TestModule} and match on a tuple instead.

Yes, there is an open issue to track this. We will be able to be more precise in Elixir v1.19 but for now the tagged tuple is the way to go.

3 Likes

Nice! Just ran a brew upgrade and saw it installed v1.18.
Thanks to everybody working on it. And i guess with the introduction of the type system i’ve chosen a good time to (finally) jump on the elixir train.

2 Likes

Typical José reply. What a great type of person he is.

Sorry, off topic. Just had to type it to get it off my chest.

3 Likes

Second this.

1 Like

Congrats to the team!

Awesome release, so many great things happening in the ecosystem :slight_smile:

I am curious as how to solve the following warning that is generated now though;
In my config.exs I have this simple line config :app, :environment, config_env() which I then use in modules @env Application.compile_env(:app, :environment).

That “breaks” in this code though.

  defp some_function(mail, subject) do
    mail
    |> subject(
      case @env do
        :dev -> "[DEV] #{subject}"
        :stage -> "[STAGE] #{subject}"
        _ -> subject
      end
    )
  end
warning: the following clause will never match:

        :stage

This both correct and wrong.

It’s correct in the context of the running process, but wrong in general, as it will be :stage in another environment. We still can’t type module attributes right? Maybe this is an anti-pattern altogether?

1 Like

Seeing similar warnings when using compile-time config for stubbing an external service module, and the stub doesn’t return error tuples in dev Mix env, but the error handling code is needed for test and prod Mix envs. It’s pretty cool to see the compiler being clever enough to detect this. But it would be nice to have a proper way to communicate to the compiler that the code is not dead code and it’s needed for different Mix env or config values.

1 Like

You could move the compile time code outside of the function.

case @env do
  :dev -> defp prepend_env_to_subject(subject), do: "[DEV] #{subject}"
  :stage -> ...
  _ -> ...
end

defp some_function(mail, subject) do
    subject = prepend_env_to_subject(subject)
    subject(mail, subject)
end
1 Like

Curious why Tuple.append is deprecated. :thinking: I updated elixir and got a warning in my AOC solution for it. I was building a tuple in reduce and appending element at the end. Now I Tuple.insert_at(acc, tuple_size(acc), element). Would that be an idiomatic replacement?

You can use :erlang.append_element(tuple, value) to skip the Elixir wrapper. It does not seem to be scheduled for deprecation in OTP 28 or 29.

Now the Elixir commit that added the deprecation seems to be related with types, so I guess you will not benefit from them if you do that.

So I would use tuple_size/1. Honestly I have never needed to append to a tuple anyway :smiley:

And thank you to the team! Great achievements this year \o/.

1 Like

Got the same recommendation on Discord, thanks, that works!