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?