Exports dependencies in Elixir 1.11
Tags: elixir
Published: 2021-01-27 20:57:57

---
date: "2021-01-28T05:57:57+0900"
title: "Exports dependencies in Elixir 1.11"
tags: [elixir]
canonical_url: https://metareal.blog/en/post/2021/01/19/elixir-v11-exports-dependencies/
series: "Changes in Elixir 1.11"
published: true
---
[Photo](https://unsplash.com/photos/I_ZGCGoodhM) by [LĂZĂRESCU ALEXANDRA](https://unsplash.com/@lazares?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText) on [Unsplash](https://unsplash.com/?utm_source=unsplash&amp;utm_medium=referral&amp;utm_content=creditCopyText)

Last year, in October 2020, [Elixir 1.11 was released](https://elixir-lang.org/blog/2020/10/06/elixir-v1-11-0-released/) 🎉 I looked at one of the compiler improvements, *Exports dependencies*, including its behavior.

## How the "*Exports dependencies"* improves compilation?

Prior to Elixir 1.11, if `module A` depends on `module B` by `import` or `require`, `module B` was considered as *Compile time dependencies* of `module A`.

Since Elixir 1.11, if `module A` uses only the public interface of `module B` (struct or public functions) is now *Exports dependencies*, and there is no need to recompile `module A` unless the public interface  of `module B` changes. [^1]

This will reduce the number of files that need to be recompiled when the source code is changed, shortening the "fix - build - run" cycle. This is great because the compilation time required for a build is an important indicator that directly affects development productivity.

## Try Exports dependencies in example

You can get an idea about Exports dependencies from coding an example. Consider the two modules below. [^2]

`moduleA.ex`

```elixir
defmodule ElixirV11.ModuleA do
  alias ElixirV11.ModuleB

  import ModuleB, only: [fetch_name: 1]

  def hello(%ModuleB{} = m) do
    case fetch_name(m) do
      {:ok, name} ->
        IO.puts("Hello, #{name}!")

      :error ->
        IO.puts("Who?")
    end
  end
end
```

`moduleB.ex`

```elixir
defmodule ElixirV11.ModuleB do
  defstruct name: nil

  def new(name), do: %__MODULE__{name: name}

  def fetch_name(%__MODULE__{name: nil}), do: :error
  def fetch_name(%__MODULE__{name: name}), do: {:ok, name}
end
```

ModuleA depends on the struct and `fetch_name/1` functions of ModuleB. This is why `import ModuleB, only: [fetch_name: 1]` in `moduleA.ex` was *Compile time dependencies* before Elixir 1.10. However, since these two are public interfaces, starting with Elixir 1.11, they will be *Exports dependencies*.

So, when you change `moduleB.ex`, you need to recompile `moduleA.ex` in Elixir 1.10, but not in Elixir 1.11.

```bash
# Elixir 1.10
$ touch lib/moduleB.ex    
$ mix compile --verbose
Compiling 1 file (.ex)
Compiled lib/moduleB.ex
Compiled lib/moduleA.ex

# Elixir 1.11
$ touch lib/moduleB.ex 
$ mix compile --verbose
Compiling 1 file (.ex)
Compiled lib/moduleB.ex
```

Let's change the implementation of `ModuleB` without changing the public interface.

`moduleB.ex`

```elixir
defmodule ElixirV11.ModuleB do
  defstruct name: 1

  def new(name), do: %__MODULE__{name: check_name!(name)}

  def fetch_name(%__MODULE__{name: nil}), do: :error
  def fetch_name(%__MODULE__{name: name}), do: {:ok, name}

  defp check_name!(%__MODULE__{name: name}) when is_binary(name), do: name
end
```

Added a private function called `check_name!/1` and changed it to be used in `new/1`. Since this does not change the public interface, it should not need to be recompiled.

```bash
# Elixir 1.10
$ mix compile --verbose
Compiling 1 file (.ex)
Compiled lib/moduleB.ex
Compiled lib/moduleA.ex

# Elixir 1.11
$ mix compile --verbose
Compiling 1 file (.ex)
Compiled lib/moduleB.ex
```

As expected, `ModuleA` do not need to be compiled in Elixir 1.11.

## Exports dependencies in a real project

[The commit](https://github.com/elixir-lang/elixir/commit/9a6db666a107b42c1242cf727eafe7638674f3cc) says as follows:

> making imports more feasible for large projects.

As this commit says, it will be easy to use `import/2` in large projects.

In fact, in the project I'm working on now, I changed the modules that manipulate Repo, which is imported at various points, to find out the number of files that are recompiled.

- **Elixir 1.10** - 48
- **Elixir 1.11** - 14

The number of recompiled files had decreased dramatically 🎉

[^1]: However, if you are using macros, it will be Compile time dependencies. This is explained in detail in **Dependencies types** on the [mix xref](https://hexdocs.pm/mix/Mix.Tasks.Xref.html) page.
[^2]: The change to the compiler is probably this. [Do not make requires/imports compile-time dependencies · elixir-lang/elixir@9a6db66](https://github.com/elixir-lang/elixir/commit/9a6db666a107b42c1242cf727eafe7638674f3cc)