Protocols in Elixir
| Define a protocol | |
| Implement it | |
| Derive cheaply | |
| Dispatch on type |
Define a protocol
A protocol declares functions that many types can implement in their own way. You define it with defprotocol and list the function heads without bodies, much like a contract for later modules.
defprotocol Sized do
def size(value)
end
Sized promises one function named size that accepts any value. No type can use it yet, because each type still needs its own implementation.
Implement it
An implementation connects a protocol to one concrete type with defimpl and the for option. Inside the block you write normal function bodies for each promised function.
defimpl Sized, for: List do
def size(list) do
length(list)
end
end
Sized.size applied to a three element list now returns 3 by counting list elements. Each new type gets its own defimpl block, so List logic never mixes with text or number logic.
Derive cheaply
Writing a full implementation for a simple struct feels heavy, so some protocols offer deriving. A derive annotation on the struct generates a stock implementation at compile time.
defmodule Point do
@derive {Inspect, only: [:x]}
defstruct [:x, :y]
end
Inspecting a Point now shows only the x field, which keeps logs short while y stays hidden. Deriving works only for protocols that ship a derivation, with Inspect as the everyday example.
Dispatch on type
Calling a protocol function picks the implementation that matches the value type at runtime. The caller names the protocol once, and Elixir routes lists one way and text another way.
defimpl Sized, for: BitString do
def size(text) do
String.length(text)
end
end
Sized.size([1, 2, 3])
Sized.size("hello")
The first call returns 3 and the second returns 5, even though both use the same Sized.size name. This runtime routing is called dispatch, and it is the heart of every protocol.
Next: Behaviours in Elixir
Article author: Arthur Isaev