Pr

Protocoles en Elixir

4 exercices

À propos de Protocoles

Les protocoles sont :

  • Un mécanisme pour obtenir du polymorphisme.
  • Définis avec defprotocol.
  • Implémentés pour un type de données donné avec 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.

Implémentation

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

Repli vers 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.

Protocoles intégrés à connaître

  • 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.
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet

Apprends Protocoles

L'entraînement est verrouillé

Déverrouille 1 exercice de plus pour t'entraîner sur Protocoles