Elixir 1.14's new `:optional` option when deriving the `Inspect` protocol

I recently discovered a new option for the Inspect protocol introduced in Elixir 1.14 — the :optional option.

Deriving the Inspect protocol

When we derive the Inspect protocol for a struct, we have three options we can use to change how the struct is presented:

  • Only show specific fields,
  • Show everything except some fields, and
  • Since Elixir 1.14, make fields optional — only showing them if they differ from their default values.

Let’s take a User struct as an example:

defmodule User do
  defstruct [:name, :email, admin: false]
end

By default, when we inspect a User struct, we’ll see this:

iex> %User{name: "Frodo", email: "frodo@baggins.com"}
%User{name: "Frodo", email: "frodo@baggins.com", admin: false}

:only certain fields

Let’s now derive the Inspect protocol and tell it to show only the :name:

defmodule User do
  @derive {Inspect, only: [:name]} # <= add this
  defstruct [:name, :email, admin: false]
end

Now, let’s see what the User struct looks like:

iex> %User{name: "Frodo", email: "frodo@baggins.com"}
#User<name: "Frodo", ...>

It only shows the name. Very nice.

Everything :except certain fields

Likewise, we can show everything except a field:

defmodule User do
  @derive {Inspect, except: [:name]} # <= add this
  defstruct [:name, :email, admin: false]
end

Let’s see what that looks like:

iex> %User{name: "Frodo", email: "frodo@baggins.com"}
#User<email: "frodo@baggins.com", admin: false, ...>

As you can see, that printed all the fields (including the default ones) except the name.

Making some fields :optional

Finally, Elixir 1.14 introduced the :optional option:

defmodule User do
  @derive {Inspect, optional: [:admin]} # <= add this
  defstruct [:name, :email, admin: false]
end

We set the :admin to be optional. Let’s see how that looks:

iex> %User{name: "Frodo", email: "frodo@baggins.com"}
%User{name: "Frodo", email: "frodo@baggins.com"}

As you can see, the User struct doesn’t show the admin field because we’re using the default.

But what if we change it to something else?

iex> %User{name: "Frodo", email: "frodo@baggins.com", admin: true}
%User{name: "Frodo", email: "frodo@baggins.com", admin: true}

Aha! Now the Inspect protocol is showing us the admin field!

When would I use that?

Turns out Elixir sometimes uses that option to hide deprecated fields. And it can be used in Ecto to hide associations when they’re not loaded!

Pretty neat, huh?

Want my latest thoughts, posts, and projects in your inbox?

    I will never send you spam. Unsubscribe any time.