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 vir junto com a definição do tipo de dados nem com a definição do protocolo. Isso permite escrever bibliotecas que os usuários podem estender facilmente para funcionar com seus próprios tipos de dados, sem precisar modificar a biblioteca.
É possível implementar protocolos para todos os tipos do Elixir: Tuple, Atom, List, BitString, Integer, Float, Function, PID, Map, Port, Reference, Any, e structs.
Os protocolos podem ser implementados para vários tipos de uma só vez:
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 a struct, o argumento :for pode ser omitido:
defmodule ShoppingCart do
defstruct [:items]
defimpl Reversible do
def reverse(shopping_cart) do
Enum.reverse(shopping_cart)
end
end
end
Any
Por padrão, invocar um protocolo para um tipo de dados para o qual ele não foi implementado vai levantar 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 de fallback, que seria usada nesse caso. Para fazer isso, defina o atributo de módulo @fallback_to_any como true ao definir o protocolo e escreva 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
Repare que, na maioria dos casos, levantar um erro é a abordagem preferível, 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 esse protocolo.Collectable - a função Enum.into/2 usa esse protocolo para inserir um enumerable em uma coleção.Inspect - você pode implementar esse protocolo para mudar como Kernel.inspect/2 representa um tipo de dados específico como uma string.String.Chars - você pode implementar esse protocolo para mudar como Kernel.to_string/1 representa um tipo de dados específico como uma string.