I protocolli sono:
defprotocol.defimpl.Un grande punto di forza dei protocolli è che l'implementazione di un protocollo per uno specifico tipo di dato non deve essere inclusa nella definizione del tipo di dato né in quella del protocollo. Questo permette di scrivere librerie che gli utenti possono estendere facilmente per lavorare con i propri tipi di dato personalizzati, senza dover modificare la libreria.
È possibile implementare i protocolli per tutti i tipi di Elixir: Tuple, Atom, List, BitString, Integer, Float, Function, PID, Map, Port, Reference, Any e le struct.
I protocolli si possono implementare per più tipi contemporaneamente:
defimpl Reversible, for: [Map, List] do
def reverse(term) do
Enum.reverse(term)
end
end
Quando si implementa un protocollo per una struct direttamente all'interno di un modulo che definisce la struct, l'argomento :for si può omettere:
defmodule ShoppingCart do
defstruct [:items]
defimpl Reversible do
def reverse(shopping_cart) do
Enum.reverse(shopping_cart)
end
end
end
Any
Per impostazione predefinita, chiamare un protocollo per un tipo di dato per cui non è stato implementato genera un errore 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
È possibile evitarlo fornendo un'implementazione di fallback che verrà usata in questo caso. Per farlo, imposta l'attributo di modulo @fallback_to_any su true quando definisci il protocollo e scrivi un'implementazione per il 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
Tieni presente che nella maggior parte dei casi è preferibile generare un errore, perché è difficile fare un'ipotesi ragionevole su un protocollo per tutti i tipi di dato.
Enumerable: le funzioni dei moduli Enum e Stream funzionano con i tipi di dato che implementano questo protocollo.Collectable: la funzione Enum.into/2 usa questo protocollo per inserire un enumerable in una collezione.Inspect: puoi implementare questo protocollo per cambiare il modo in cui Kernel.inspect/2 rappresenta come stringa uno specifico tipo di dato.String.Chars: puoi implementare questo protocollo per cambiare il modo in cui Kernel.to_string/1 rappresenta come stringa uno specifico tipo di dato.