What's New in Elixir 1.13
Tags: elixir
Published: 2021-12-21 12:53:01

On December 3rd, the Elixir core team announced the latest Elixir release, 1.13. In this post, we'll explore some of the more exciting changes and new features, and discuss their impact on the Elixir community.

## All About Tooling

Elixir 1.13 comes packed with lots of goodies and improvements designed to make the day-to-day life of Elixir developers easier. With new additions like semantic code recompilation, better support for auto-complete in IEx, an API for custom code formatting, and more, quality of life for Elixir developers is going to greatly increase. Let's dive into some of the latest developments now.

### Making IEx Better

Elixir 1.13 comes with a number of changes that enhance the IEx experience. The introduction of the new [`Code.Fragment`](https://hexdocs.pm/elixir/master/Code.Fragment.html) module greatly improves the autocomplete story for Elixir, making your work in IEx even easier. `Code.Fragment` provides conveniences for analyzing fragments of textual code and returning information about those fragments. Practically speaking, this means that IEx now supports autocomplete of sigils, structs, and paths. Let's take a look at some examples.

Previously, attempting to autocomplete the `~` character didn't lead to anything too useful:

```iex
iex(1)> ~
!/1                           !=/2
!==/2                         %/2
%{}/1                         &&/2
&/1                           */2
++/2                          +/1
+/2                           --/2
-/1                           -/2
..///3                        ../2
./2                           //2
::/2                          </2
<<>>/1                        <=/2
<>/2                          =/2
==/2                          ===/2
=~/2                          >/2
>=/2                          @/1
^/1                           __CALLER__/0
__DIR__/0                     __ENV__/0
__MODULE__/0                  __STACKTRACE__/0
__aliases__/1                 __block__/1
abs/1                         alias!/1
alias/2                       and/2
apply/2                       apply/3
b/1                           binary_part/3
binding/0                     binding/1
bit_size/1                    break!/1
break!/2                      break!/3
break!/4                      breaks/0
byte_size/1                   c/1
c/2                           case/2
cd/1                          ceil/1
clear/0                       cond/1
continue/0                    def/1
def/2                         defdelegate/2
defexception/1                defguard/1
defguardp/1                   defimpl/2
defimpl/3                     defmacro/1
defmacro/2                    defmacrop/1
defmacrop/2                   defmodule/2
defo
# ... and the list goes on
```

Now, if you attempt the same autocomplete, you'll see this:

```elixir
iex> ~
~C (sigil_C)    ~D (sigil_D)    ~N (sigil_N)    ~R (sigil_R)
~S (sigil_S)    ~T (sigil_T)    ~U (sigil_U)    ~W (sigil_W)
~c (sigil_c)    ~r (sigil_r)    ~s (sigil_s)    ~w (sigil_w)
```

A helpful list of sigil functions.

Similarly, in earlier Elixir versions, trying to autocomplete a struct would yield the following:

- First, assume you have a struct, `User`, defined like this:

```elixir
defmodule User do
  defstruct [:name, :age]
end
```

- Attempt IEx autocomplete by hitting `%Use<TAB>`:

```iex
iex> %Use
 %User.__struct__
__struct__/0    __struct__/1
```

Not exactly a helpful look at the struct. Let's see what the same operation yields in Elixir 1.13:

```iex
iex> %User{
name:    age:
```

Here, we get a helpful look at all of the fields of our struct.

These autocomplete improvements don't just make IEx better. Your autocomplete experience in your text editor of choice will also improve. With additional changes like new compilation tracers and new `Module` functions for retrieving module metadata, we can expect to see new code intelligence features come out that will improve your text editor experiences with Elixir even further.

### Faster Elixir Code Recompilation

With Elixir's new semantic code recompilation, your code will be recompiled less frequently. This means a faster development life cycle, especially when it comes to working with large Elixir applications.

Prior to Elixir 1.13, your _entire_ Elixir app would be recompiled whenever any of the `mix.exs`, `config/config.exs`, `src/*`, or `mix.lock` files changed. Now, thanks to semantic code compilation, your code will recompile _only_ when you change the compilation options in the `mix.exs` file or the configuration in the `config/config.exs` file.

Code compilation has gotten smarter in other ways too. Not only will your application be recompiled less frequently, but certain changes will now trigger recompilation of smaller parts of your app, or no recompilation at all.

For example, now if the size _and_ digest of a file stay the same, the file will not be recompiled. This means switching or rebasing git branches will trigger far fewer recompilations. Additionally, changing compile-time config files (like `config/config.exs`) will _only_ recompile any files that depend on the reconfigured files. Practically speaking, this means that, for example, bumping your Phoenix version in `mix.exs` will _only_ recompile `lib/my_app_web` and _not_ `lib/my_app`.

Semantic compilation is accompanied by some improvements to the `mix xref` task. This task gives you information about your files and how they depend on each other. Here's an abbreviated look at running `mix xref` with the `graph` option in a standard Phoenix app:

```elixir
// ♥ mix xref graph
lib/pento.ex
lib/pento/accounts.ex
├── lib/pento/accounts/user.ex (export)
├── lib/pento/accounts/user_notifier.ex
├── lib/pento/accounts/user_token.ex (export)
└── lib/pento/repo.ex
```

This tells us that all of the files listed _under_ `lib/pento/accounts/ex` depend on `account.ex` at compilation time. The task already gives us some great tooling to identify _that_ a compile-time dependency exists, but doesn't really provide any insight into _why_ such a dependency exists. So, if you needed to track down the source of an inappropriate compile-time dependency when that source is non-obvious (if it comes from a macro, for example), this tool used to fall short. Until now!

Elixir 1.13 adds a new option to the `mix xref` task - the `trace` option. Now, you can run `mix xref trace <file name>` and see a list of exactly where all of our compile time dependencies, exports, and runtime dependencies come from within that file, down to the exact line number. Let's check it out:

```iex
lib/pento/accounts.ex:26: call Pento.Repo.get_by/2 (runtime)
lib/pento/accounts.ex:43: call Pento.Repo.get_by/2 (runtime)
lib/pento/accounts.ex:44: call Pento.Accounts.User.valid_password?/2 (runtime)
lib/pento/accounts.ex:61: call Pento.Repo.get!/2 (runtime)
lib/pento/accounts.ex:78: struct Pento.Accounts.User (export)
lib/pento/accounts.ex:79: call Pento.Accounts.User.registration_changeset/2 (runtime)
lib/pento/accounts.ex:80: call Pento.Repo.insert/1 (runtime)
lib/pento/accounts.ex:92: struct Pento.Accounts.User (export)
lib/pento/accounts.ex:93: call Pento.Accounts.User.registration_changeset/3 (runtime)
lib/pento/accounts.ex:108: call Pento.Accounts.User.email_changeset/2 (runtime)
lib/pento/accounts.ex:126: call Pento.Accounts.User.email_changeset/2 (runtime)
lib/pento/accounts.ex:127: call Pento.Accounts.User.validate_current_password/2 (runtime)
lib/pento/accounts.ex:140: call Pento.Accounts.UserToken.verify_change_email_token_query/2 (runtime)
lib/pento/accounts.ex:141: struct Pento.Accounts.UserToken (export)
lib/pento/accounts.ex:141: call Pento.Repo.one/1 (runtime)
# ... I'm truncating this output for brevity, but there's more!
```

This gives us a powerful tool for understanding the interdependencies in our code and eliminating them where necessary.

### Better Error Messages in Elixir

Elixir 1.13 also brings us improved error messages for both the `SyntaxError` and `TokenMissing` errors. Such error messages now include code snippets that show you the exact source of your error. This is just one more feature to improve your development experience and make hunting down bugs even easier. Let's try it out.

Assuming I've added the following bad code to my project:

```elixir
def add_nums(num1, num2) do
  num1 + *num2
end
```

I should see the following error when I try to do something like run `iex -S mix`:

```iex
== Compilation error in file lib/pento/accounts.ex ==
** (SyntaxError) lib/pento/accounts.ex:26:9: syntax error before: '*'
    |
 26 |     num1 + *num2
    |         ^
    (elixir 1.13.0) lib/kernel/parallel_compiler.ex:346: anonymous fn/5 in Kernel.ParallelCompiler.spawn_workers/7
```

### Extensible Formatting

The latest Elixir release extends the capabilities of the Elixir formatter so you can implement custom formatting for any embedded code.

Consider the following scenario. You have a LiveView app that renders lots of HEEx templates:

```elixir
# lib/my_app_live/user_live.ex

def render(assigns)
  ~H"""
  <h2> User Profile
    </h2>
  """
end
```

You can see that the code in our HEEx template is formatted improperly, but since its _not_ actually invalid, HEEx won't complain about it. When we run `mix format`, the code embed _within_ the `sigil_H` won't be formatted.

Now, though, Elixir provides an API through which we can implement and apply custom formatters for embedded code. Here's how it works.

You'll define a custom formatter plugin module along these lines:

```elixir
defmodule MixHEExFormatter do
  @behaviour Mix.Tasks.Format

  def features(_opts) do
    [sigils: [:H], extensions: [".ex",".heex"]]
  end

  def format(contents, opts) do
    # logic that formats HEEx
  end
end
```

Then, you'll add your plugin to your app in `.formatter.exs`, like this:

```elixir
[
  plugins: [MixHEExFormatter],
  # Remember to update the inputs list to include the new extensions
  inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}", "lib/my_app_live/live/*.{ex,.heex}"]
]
```

With this new functionality, we can expect to see some upcoming development of custom formatters for common types of embedded code. The Zigler project already has an issue open for a [custom `~Z` formatter](https://github.com/ityonemo/zigler/issues/287), and I hope to see some development for a `~H` soon.

### AST-Based Tooling

Another exciting new piece of functionality comes by way of two new functions added to the `Code` module. [`Code.quoted_to_algebra/2`](https://hexdocs.pm/elixir/Code.html#quoted_to_algebra/2) and [`Code.string_to_quoted_with_comments/2](https://hexdocs.pm/elixir/Code.html#string_to_quoted_with_comments/2) will enable you to fetch the Elixir AST with original comments and convert it into formatted code.

This capability will empower developers to create tools for custom formatting of Elixir source code.

### Better Testing in Elixir

Elixir 1.13 ships with a few new ExUnit niceties for debugging tests. ExUnit will now show a hint when comparing two different but equivalent strings, making debugging minor discrepancies between strings in your ExUnit expectations much less of a headache.

ExUnit also provides two new functions:

- [`ExUnit.CaptureIO.with_io/3`](https://hexdocs.pm/ex_unit/1.13.0/ExUnit.CaptureIO.html#with_io/3) allows you to apply a function to captured IO output
- [`ExUnit.CaptureLog.with_log/2`](https://hexdocs.pm/ex_unit/1.13.0/ExUnit.CaptureLog.html#with_log/2) invokes the given function and returns the result and a captured log.

Here's the new example from the docs:

```elixir
`{result, log} =`
  `with_log(fn ->`
    `Logger.error("log msg")`
    `2 + 2`
  `end)`

`assert result == 4`
`assert log =~ "log msg"`
```

With this, we can easily test the result of function invocations _and_ validate assertions against any log statements made during those function invocations.

Lastly, mix supports a new option, `--profile-require=time` - so that you can run `mix test --profile-require=time` to debug test suite load times.

## New Elixir Language Features

In addition to this impressive list of new tooling capabilities, Elixir 1.13 also ships with some exciting new pieces of Elixir functionality. We'll take a look at just of few of these changes here.

The [`Enum.slide/3`](https://hexdocs.pm/elixir/Enum.html#slide/3) function takes in arguments of an enumerable, index or range of indices, and the insertion index. It "slides" the index or range of indices to the target insertion index, like this:


```elixir

`#Slide a single element`
`Enum.slide([:a, :b, :c, :d, :e, :f, :g], 5, 1)`
`#=> [:a, :f, :b, :c, :d, :e, :g]`

```

Here, we "slid" the element living at index 5, `:f`, and inserted it instead at index 1.

The [`List.keyfind/4`](https://hexdocs.pm/elixir/List.html#keyfind/4) function receives a list of tuples and returns the first tuple where the element at the position in the tuple matches the given key. For example:

```elixir
`List.keyfind([a: 1, b: 2], :a, 0)`
`# => {:a, 1}`
```

The [`Keyword.validate/2`](https://hexdocs.pm/elixir/1.13.0/Keyword.html#validate!/2) function lets you validate a keyword list to ensure that it contains only the provided set of values.

You can now also filter a keyword list with the [`Keyword.filter/2`](https://hexdocs.pm/elixir/1.13.0/Keyword.html#filter/2) function and map over a keyword list with the [`Keyword.map/2`](https://hexdocs.pm/elixir/1.13.0/Keyword.html#map/2) function.

## Wrap Up

This was a quick overview of the highlights from Elixir 1.13, but there's lots more to see, and you can [check out the full changelog](https://hexdocs.pm/elixir/1.13.0/changelog.html). This latest Elixir release means that quality of life for Elixir developers is getting even better.

New autocomplete and associated features make writing Elixir code faster, semantic code recompilation means faster development cycles, and new testing conveniences and Elixir language capabilities give us even more tools to get the job done.

We're excited to see what the community builds on top of all of this functionality.

Happy coding!

**P.S. If you'd like to read Elixir Alchemy posts as soon as they get off the press, [subscribe to our Elixir Alchemy newsletter and never miss a single post](/elixir-alchemy)!**