Os protocolos são:
defprotocol.defimpl.Uma grande vantagem dos protocolos é que a implementação de um protocolo para um tipo de dados específico não precisa de estar incluída na definição do tipo de dados nem na definição do protocolo. Isto permite escrever bibliotecas que os seus utilizadores podem estender facilmente para funcionar com os seus próprios tipos de dados, sem terem de modificar a biblioteca.
É possível implementar protocolos para todos os tipos de Elixir: Tuple, Atom, List, BitString, Integer, Float, Function, PID, Map, Port, Reference, Any e structs.
Os protocolos podem ser implementados para vários tipos ao mesmo tempo:
defimpl Reversible, for: [Map, List] do
def reverse(term) do
Enum.reverse(term)
end
end
Ao implementar um protocolo para uma struct diretamente dentro de um módulo que define essa struct, podes omitir o argumento :for:
defmodule ShoppingCart do
defstruct [:items]
defimpl Reversible do
def reverse(shopping_cart) do
Enum.reverse(shopping_cart)
end
end
end
Any
Por predefinição, invocar um protocolo para um tipo de dados para o qual ele não foi implementado gera um erro Protocol.UndefinedError:
to_string({})
# => ** (Protocol.UndefinedError) protocol String.Chars not implemented for {} of type Tuple
# (elixir 1.10.3) lib/string/chars.ex:3: String.Chars.impl_for!/1
# (elixir 1.10.3) lib/string/chars.ex:22: String.Chars.to_string/1
É possível evitar isso fornecendo uma implementação alternativa que seria usada nesse caso. Para isso, define o atributo de módulo @fallback_to_any como true ao definir o protocolo e escreve uma implementação para o tipo Any:
defprotocol Reversible do
@fallback_to_any true
def reverse(term)
end
defimpl Reversible, for: Any do
def reverse(term) do
term
end
end
Tem em atenção que, na maioria dos casos, é preferível gerar um erro, porque é difícil fazer uma suposição razoável sobre um protocolo para todos os tipos de dados.
Enumerable - as funções dos módulos Enum e Stream funcionam com tipos de dados que implementam este protocolo.Collectable - a função Enum.into/2 usa este protocolo para inserir um enumerável numa coleção.Inspect - podes implementar este protocolo para alterar a forma como Kernel.inspect/2 representa um tipo de dados específico como string.String.Chars - podes implementar este protocolo para alterar a forma como Kernel.to_string/1 representa um tipo de dados específico como string.