🎓 A1Learner
🔍
0 of 46 lessons completed

Elixir Deriving Protocols

Deriving a protocol is a shortcut that gives a struct an implementation without writing a separate implementation module. The struct states the protocol with @derive, and that protocol then handles the struct.

Inspect deriving limits the printed fields

User derives Inspect with only name. The printed form shows name and hides the rest.

defmodule User do
  @derive {Inspect, only: [:name]}
  defstruct name: "Ana", age: 31
end

IO.inspect(struct(User, name: "Bo", age: 40))

A custom protocol can be derived from its Any implementation

Labeller has an Any implementation. A struct that derives Labeller uses that implementation.

defprotocol Labeller do
  @fallback_to_any true
  def label(data)
end

defimpl Labeller, for: Any do
  def label(_data), do: "any"
end

defimpl Labeller, for: BitString do
  def label(s), do: "text " <> s
end

defmodule Box do
  @derive Labeller
  defstruct content: "toy"
end

IO.inspect(Labeller.label(struct(Box, content: "ball")))
IO.inspect(Labeller.label("hi"))

Inspect deriving can hide a field

Item derives Inspect with except secret. The printed form shows name and hides secret.

defmodule Item do
  @derive {Inspect, except: [:secret]}
  defstruct name: "key", secret: "shh"
end

IO.inspect(struct(Item))

Example

defprotocol Labeller do
  @fallback_to_any true
  def label(data)
end

defimpl Labeller, for: Any do
  def label(_data), do: "any"
end

defmodule Parcel do
  @derive {Inspect, only: [:id]}
  @derive Labeller
  defstruct id: 7, content: "book"
end

IO.inspect(struct(Parcel))
IO.inspect(Labeller.label(struct(Parcel)))

Key points

  • Derive attribute: @derive before defstruct names a protocol the struct should implement.
  • Inspect control: @derive with Inspect and only limits the printed fields to the named fields.
  • Inspect hiding: @derive with Inspect and except hides the named fields from the printed form.
  • Any implementation: a protocol with an implementation for Any can serve as the derived implementation for a struct.
  • Fallback: setting fallback to Any lets plain values use the Any implementation too.

In this example, Parcel derives Inspect with only id, so its printed form shows id alone, and it also derives Labeller, so labelling it uses the Any implementation.

Note

deriving changes what a protocol does for a struct, not the struct itself.

Try it Yourself

Edit the code, then press Run.

Running is simulated

Exercise

What does @derive {Inspect, only: [:name]} do to a struct?