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?