Protocols
Protocols provide polymorphism based on data type (similar to interfaces/type classes).
Protocols provide polymorphism based on data type (similar to interfaces/type classes).
Types: Atom, BitString, Float, Function, Integer, List, Map, PID, Port, Reference, Tuple, Any, plus structs.
defprotocol Size do
@doc "Returns the size of a data structure"
def size(data)
end
defimpl Size, for: BitString do
def size(string), do: byte_size(string)
end
defimpl Size, for: Map do
def size(map), do: map_size(map)
end
defimpl Size, for: Tuple do
def size(t), do: tuple_size(t)
end
defimpl Size, for: User do # implement for a struct
def size(_), do: 1
end
Size.size("hello") # 5
Size.size(%{a: 1}) # 120.1 Fallback to Any and deriving
defprotocol Describe do
@fallback_to_any true
def describe(term)
end
defimpl Describe, for: Any do
def describe(_), do: "something"
end20.2 Built-in protocols
Enum / Stream functions Enum.into, for... into: to_string/1, interpolation inspect/1, IEx output
JSON.Encoder (1.18+) / Jason.Encoder
defimpl String.Chars, for: User do
def to_string(user), do: "#{user.name} <#{user.email}>"
end
"User: #{user}"20.3 Implementing Enumerable for a custom collection
Protocol consolidation happens in Mix builds for fast dispatch.
defmodule Countdown do
defstruct [:from]
end
defimpl Enumerable, for: Countdown do
def reduce(_cd, {:halt, acc}, _f), do: {:halted, acc}
def reduce(cd, {:suspend, acc}, f), do: {:suspended, acc, &reduce(cd, &1, f)}
def reduce(%Countdown{from: 0}, {:cont, acc}, _f), do: {:done, acc}
def reduce(%Countdown{from: n}, {:cont, acc}, f),
do: reduce(%Countdown{from: n - 1}, f.(n, acc), f)
def count(%Countdown{from: n}), do: {:ok, n}
def member?(%Countdown{from: n}, x), do: {:ok, is_integer(x) and x in 1..n}
def slice(_), do: {:error, __MODULE__}
end
Enum.to_list(%Countdown{from: 3}) # [3, 2, 1]