Треки
/
Elixir
Elixir
/
Салабус
/
Протоколи
Пр

Протоколи у Elixir

4 вправи

Про концепцію Протоколи

Протоколи:

  • Механізм для досягнення поліморфізму.
  • Визначаються за допомогою defprotocol.
  • Реалізуються для конкретного типу даних за допомогою defimpl.

Велика перевага протоколів у тому, що реалізацію протоколу для конкретного типу даних не потрібно додавати ні до визначення цього типу даних, ні до визначення самого протоколу. Це дає змогу писати бібліотеки, які їхні користувачі можуть легко розширювати для роботи зі своїми власними типами даних, не змінюючи саму бібліотеку.

Реалізація

Протоколи можна реалізувати для всіх типів Elixir: Tuple, Atom, List, BitString, Integer, Float, Function, PID, Map, Port, Reference, Any і структур.

Протоколи можна реалізувати одразу для кількох типів:

defimpl Reversible, for: [Map, List] do
  def reverse(term) do
    Enum.reverse(term)
  end
end

Коли ми реалізуємо протокол для структури безпосередньо всередині модуля, який визначає цю структуру, аргумент :for можна не вказувати:

defmodule ShoppingCart do
  defstruct [:items]

  defimpl Reversible do
    def reverse(shopping_cart) do
      Enum.reverse(shopping_cart)
    end
  end
end

Запасний варіант для Any

Типово виклик протоколу для типу даних, для якого його не реалізовано, призведе до помилки 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

Цього можна уникнути, надавши запасну реалізацію, яку буде використано в такому разі. Для цього під час визначення протоколу встановімо атрибут модуля @fallback_to_any у true і напишімо реалізацію для типу Any:

defprotocol Reversible do
  @fallback_to_any true
  def reverse(term)
end

defimpl Reversible, for: Any do
  def reverse(term) do
    term
  end
end

Зауважмо, що в більшості випадків краще спричинити помилку, адже важко зробити розумне припущення щодо протоколу для всіх типів даних.

Вбудовані протоколи, варті уваги

  • Enumerable - функції з модулів Enum і Stream працюють із типами даних, які реалізують цей протокол.
  • Collectable - функція Enum.into/2 використовує цей протокол, щоб вставити перелічуване значення в колекцію.
  • Inspect - ми можемо реалізувати цей протокол, щоб змінити те, як Kernel.inspect/2 подає конкретний тип даних як рядок тексту (англ. string).
  • String.Chars - ми можемо реалізувати цей протокол, щоб змінити те, як Kernel.to_string/1 подає конкретний тип даних як рядок тексту.
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Протоколи

Практика заблокована

Розблокуйте ще 1 вправу, щоб практикувати концепцію Протоколи