Les protocoles sont :
defprotocol.defimpl.Le grand atout des protocoles, c'est que l'implémentation d'un protocole pour un type de données donné n'a pas besoin d'être livrée avec la définition de ce type de données ni avec celle du protocole. Cela permet d'écrire des bibliothèques que leurs utilisateurs peuvent facilement étendre pour qu'elles fonctionnent avec leurs propres types de données, sans avoir à modifier la bibliothèque.
On peut implémenter des protocoles pour tous les types d'Elixir : Tuple, Atom, List, BitString, Integer, Float, Function, PID, Map, Port, Reference, Any, ainsi que pour les structures.
Un protocole peut être implémenté pour plusieurs types à la fois :
defimpl Reversible, for: [Map, List] do
def reverse(term) do
Enum.reverse(term)
end
end
Lorsqu'on implémente un protocole pour une structure directement à l'intérieur du module qui définit cette structure, on peut omettre l'argument :for :
defmodule ShoppingCart do
defstruct [:items]
defimpl Reversible do
def reverse(shopping_cart) do
Enum.reverse(shopping_cart)
end
end
end
Any
Par défaut, invoquer un protocole pour un type de données pour lequel il n'a pas été implémenté lève une erreur 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
On peut l'éviter en fournissant une implémentation de repli qui sera utilisée dans un tel cas. Pour cela, définis l'attribut de module @fallback_to_any à true au moment de définir le protocole, puis écris une implémentation pour le type Any :
defprotocol Reversible do
@fallback_to_any true
def reverse(term)
end
defimpl Reversible, for: Any do
def reverse(term) do
term
end
end
Note que dans la plupart des cas, lever une erreur est l'approche à privilégier, car il est difficile de faire une supposition raisonnable à propos d'un protocole pour tous les types de données.
Enumerable : les fonctions des modules Enum et Stream fonctionnent avec les types de données qui implémentent ce protocole.Collectable : la fonction Enum.into/2 utilise ce protocole pour insérer un énumérable dans une collection.Inspect : tu peux implémenter ce protocole pour changer la façon dont Kernel.inspect/2 représente un type de données donné sous forme de string.String.Chars : tu peux implémenter ce protocole pour changer la façon dont Kernel.to_string/1 représente un type de données donné sous forme de string.