プロ

プロトコル の 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.Chars:このプロトコルを実装すると、Kernel.to_string/1が特定のデータ型を文字列として表現する方法を変更できます。
GitHubで編集 リンクは新しいウィンドウまたはタブで開きます

プロトコルを学習する

練習はロックされています

プロトコルを練習するには、あと1個の演習のロックを解除してください