Protocols enable polymorphism in Elixir. Define protocols with defprotocol
:
defprotocol Log do
def log(value, opts)
end
Implement a protocol with defimpl
:
require Logger
# User and Post are custom structs
defimpl Log, for: User do
def log(user, _opts) do
Logger.info "User: #{user.name}, #{user.age}"
end
end
defimpl Log, for: Post do
def log(user, _opts) do
Logger.info "Post: #{post.title}, #{post.category}"
end
end
Instead of sharing protocol implementation with maps, structs require their own protocol implementation.
With the above implementations, we can do:
iex> Log.log(%User{name: "Yos", age: 23})
22:53:11.604 [info] User: Yos, 23
iex> Log.log(%Post{title: "Protocols", category: "Protocols"})
22:53:43.604 [info] Post: Protocols, Protocols
Protocols let you dispatch to any data type, so long as it implements the protocol. This includes some built-in types such as Atom
, BitString
, Tuples
, and others.
Other Examples
Poison allows developers to implement Encoders
for their own structs for serializing them to JSON, by implementing the Poison.Encoder
protocol.
defimpl Poison.Encoder, for: Person do
def encode(%{name: name, age: age}, options) do
Poison.Encoder.BitString.encode("#{name} (#{age})", options)
end
end
Additional reading: