[Elixir]EnumerableというProtocol


Elixirのソースコードに関数を追加したいと思ってコードを追いかけていたら Enumerableというモジュールらしいものが出てきたのでなにか調べてみた。

追いかけてたら突如出てきた Enumerabe.slice

2163  def random(enumerable) do
2164    result =
2165      case Enumerable.slice(enumerable) do

なぞ

ElixirにはEnumモジュールが存在するのになんでいるのか?
最初これがわからなかった。
IExでこのコードを試してみたところこんな結果になった

iex(2)> Enumerable.slice([1,2,3])
{:error, Enumerable.List}
iex(3)> Enumerable.slice(%{a: 10, b: 20})
{:ok, 2, #Function<0.84261211/2 in Enumerable.Map.slice/1>}

なんだぁお前??

結論

結論は Protocol だった。

ProtocolというのはElixirSchoolさんによると以下のようなものです。

プロトコルはElixirにおいてポリモルフィズムを獲得する手段です。

ちなみにEnumerableは以下の関数を持っているといいらしいです。

  • count(enumerable)

    • Retrieves the number of elements in the enumerable.
  • member?(enumerable, element)

    • Checks if an element exists within the enumerable.
  • reduce(enumerable, acc, fun)

    • Reduces the enumerable into an element.
  • slice(enumerable)

    • Returns a function that slic

Functionに実装されたEnumerable.reduceの話

このenum.exを見ているとだいぶ下の方に defimplの項目が出てくるのですがそこに Functionに対して実装した部分があるんですよね


3992 defimpl Enumerable, for: Function do
3993  def count(_function), do: {:error, __MODULE__}
3994  def member?(_function, _value), do: {:error, __MODULE__}
3995  def slice(_function), do: {:error, __MODULE__}
3996
3997  def reduce(function, acc, fun) when is_function(function, 2), do: function.(acc, fun)
3998
3999  def reduce(function, _acc, _fun) do
4000    raise Protocol.UndefinedError,
4001      protocol: @protocol,
4002      value: function,
4003      description: "only anonymous functions of arity 2 are enumerable"
4004  end
4005 end

なんだこれ
確かに count, member?, slice, reduce関数を満たしているけど実際に実装されてるのは reduce関数だけ..

ちなみに使ってみるとこんな感じ

iex(8)> Enumerable.reduce(fn acc, f -> Enum.map(1..10, & f.(&1, acc)) end, [], fn n, acc -> acc ++ [n] end)
[[1], [2], [3], [4], [5], [6], '\a', '\b', '\t', '\n']

僕の想像力ではなんとなく2次元配列を良しなにreduceできるのでは とか思ったりするんですが詳しいことはよくわからん...
もし詳しい使い方をご存じの方おられたら教えて下さい