Protocols
Introduction
Essentially an OO interface (for data) done right. They do, however, have the following benefits:
- The implementation is decoupled from both the protocol definition (the interface equivalent) and the struct (the class equivalent) it is implemented for.
- As functions are called through the interface name, there cannot be function name clashes. In other words, a struct may implement two interfaces that both declare a function with the same specification, and have different implementations for these.
Documentation:
Example
Protocol:
defprotocol Shape do
@spec circumference(t) :: number
def circumference(shape)
end
Types:
defmodule Circle do
defstruct r: 0.0
end
defmodule Rectangle do
defstruct width: 0.0, height: 0.0
def circumference(rectangle) do
2 * (rectangle.width + rectangle.height)
end
end
Implementations:
defimpl Shape, for: Circle do
def circumference(circle) do
2 * :math.pi() * circle.r
end
end
defimpl Shape, for: Rectangle do
def circumference(rectangle) do
Rectangle.circumference(rectangle)
end
end
Use:
shapes = [
%Circle{r: 0},
%Circle{r: 0.5},
%Circle{r: 1},
%Rectangle{width: 0.5, height: 0.5},
%Rectangle{width: 0.5},
%Rectangle{}
]
for shape <- shapes do
IO.puts(Shape.circumference(shape))
end
Default Implementations
A protocol can be defined for Any
:
defimpl Shape, for: Any do
def circumference(_shape), do: 0.0
end
A struct can be told to use this default implementation:
defmodule Point do
@derive [Shape]
defstruct x: 0.0, y: 0.0
end
A protocol can declare that structs lacking a specific implementation can fall back to the default implementation:
defprotocol ShapeWithDefault do
@fallback_to_any true
@spec circumference(t) :: number
def circumference(shape)
end
defimpl ShapeWithDefault, for: Any do
def circumference(_shape), do: 42.0
end
Demonstration:
defimpl ShapeWithDefault, for: Circle do
def circumference(circle) do
2 * :math.pi() * circle.r
end
end
for shape <- shapes do
IO.puts(ShapeWithDefault.circumference(shape))
end
Built-In Protocols
The String.Chars
protocol defined the to_string/0
function. that is used for string interpolation:
"#{42}"
The Inspect
protocol is responsible for converting any data structure into a human-readable textual representation. It is used by this interpreter:
%Rectangle{width: 1, height: 2}