みんな大好きパイプラインの正体を追え！

この記事は [Elixir Advent Calendar 2019](https://qiita.com/advent-calendar/2019/elixir) 6日目の記事です

昨日は @sym_num さんで [Elixirで並列Lispインタプリタ、コンパイラを作るお話](https://qiita.com/sym_num/items/e41e85447a9de61bf6b8)でした！

---

Elixirの大好きなところを言ってみてって言われたら多すぎて困っちゃうと思いますが、その中でも パイプライン演算子 をあげる錬金術士さんは多いんじゃないでしょうか :question: 

今日はそんなみんな大好きパイプの正体を追ってみたいと思います :eyes: 

# そもそもパイプって？

参考: https://elixirschool.com/ja/lessons/basics/pipe-operator/

`|>` ←こいつです

前の式の結果を次の関数呼び出しの第一引数に渡せるという便利なやつです。

```exs
foo(bar(baz(new_function(other_function()))))
# ↓
other_function() |> new_function() |> baz() |> bar() |> foo()
```

よくありがちな関数の結果を次の関数に渡す・・・というのをスマートにおしゃれに書くことができます:ribbon:

このパイプライン演算子とElixirの特徴の関数型言語というのがすごいマッチしていて、気持ちよくプログラミングすることができますね :sparkles:

# 早速パイプの実装を発見した我々だが・・・？

というわけで、 GithubにあるElixirのリポジトリを探検してみたところ、すぐに `|>` を見つけることができました :eyes:

```exs:lib/elixir/lib/kernel.ex
  defmacro left |> right do
     [{h, _} | t] = Macro.unpipe({:|>, [], [left, right]})
     fun = fn {x, pos}, acc ->
       Macro.pipe(acc, x, pos)
     end

     :lists.foldl(fun, h, t)
   end
```

シンプル！！けど難しい！！

まずは少しずつ解読をすすめていくことにします。

## defmacro left |> right do

Elixirでは `defmacro` を使うことで、マクロを定義して、メタプログラミングすることができるようです。

参考: https://elixirschool.com/ja/lessons/advanced/metaprogramming/

そして defmacro に渡ってくる値は構文木(AST)になるようです。

動作を確認するために適当に以下のようなコードを書いてみました

```exs:main.exs
defmodule Sample do
  defmacro left | right do
    IO.inspect left
    IO.inspect right
    left
  end
end

defmodule Main do
  require Sample
  import Sample

  def main do
    div(100, 5) | div(2)
  end
end

Main.main

# $ elixir main.exs
# {:div, [line: 14], [100, 5]}
# {:div, [line: 14], [2]}
```

たしかに `left` と `right` には構文木が渡されていますね :thinking:
興味深いのは `div(2)` という普通だったら、怒られるような関数呼び出しを書いていても特に怒られずに実行できているところです。
このコードではマクロに構文木を渡しているだけで関数の評価までいっていないので怒られていないって感じでしょうか :thinking:

## [{h, _} | t] = Macro.unpipe({:|>, [], [left, right]})

`Macro.unpipe/1` は `lib/elixir/lib/macro.ex` に実装があります

```exs:lib/elixir/lib/macro.ex
  def unpipe(expr) do
    :lists.reverse(unpipe(expr, []))
  end

  defp unpipe({:|>, _, [left, right]}, acc) do
    unpipe(right, unpipe(left, acc))
  end

  defp unpipe(other, acc) do
    [{other, 0} | acc]
  end
```

こちらもシンプルですね！！

試しに iex を作って直接実行してみました

```exs
iex> quote do: 100 |> div(5) |> div(2)
{:|>, [], [{:|>, [], [100, {:div, [], [5]}]}, {:div, [], [2]}]}

iex> Macro.unpipe(quote do: 100 |> div(5) |> div(2))
[{100, 0}, {{:div, [], [5]}, 0}, {{:div, [], [2]}, 0}]
```

どうやらパイプラインの構文木をシンプルなタプルのリストに変換してくれるみたいです。

タプルの2番目の`0`は前の要素が何番目の引数に入るかを示しているようです。
`{{:div, [], [5]}, 0}` だったらその前の要素の `100` が 0番目(第1引数) になるということでみたいですね！


## fun = fn {x, pos}, acc ... :lists.foldl(fun, h, t)

ここまで来たら残りは一気に行きます！

```exs
     fun = fn {x, pos}, acc ->
       Macro.pipe(acc, x, pos)
     end

     :lists.foldl(fun, h, t)
```

[foldl](https://hexdocs.pm/elixir/List.html#foldl/3) はReduceみたいなもので、配列の左側から渡された関数を適用していくみたいですね。

ここでは、unpipeして簡略されたASTに `Macro.pipe/3` を適用していってます

`Macro.pipe/3` を覗いたところ、今回主に実行されそうなのは以下の部分でした。


```exs:lib/elixir/lib/macro.ex

  def pipe(expr, {op, line, args} = op_args, integer) when is_list(args) do
    cond do
      is_atom(op) and Identifier.unary_op(op) != :error ->
        raise ArgumentError,
              "cannot pipe #{to_string(expr)} into #{to_string(op_args)}, " <>
                "the #{to_string(op)} operator can only take one argument"

      is_atom(op) and Identifier.binary_op(op) != :error ->
        raise ArgumentError,
              "cannot pipe #{to_string(expr)} into #{to_string(op_args)}, " <>
                "the #{to_string(op)} operator can only take two arguments"

      true ->
        {op, line, List.insert_at(args, integer, expr)}
    end
  end
```

`{op, line, List.insert_at(args, integer, expr)}` はい、この部分ですね。

構文木のタプルの3番目は引数の配列になるのですが、そちらに差し込んでいますね。

なので最終的に `100 |> div(5)` の構文木は `{:div, _, [100, 5]}` となります！

これは `div(100, 5)` の構文木と一緒ですね :sparkles: 

```exs
iex> quote do: div(100, 5)
{:div, [_], [100, 5]}
```


# まとめ

というわけで、パイプライン演算子 `|>` の正体はただのマクロでした！！

パイプライン演算子だけではなく、Elixirではほとんどの演算子がマクロによって定義されているみたいですね。

中身を覗いてすごい読みやすく実装されていて驚きました。

こんなコードを書いていけるようになりたいですね :sparkles: 


---

明日は @takasehideki さんの[「Erlang Ecosystem Foundationの紹介」](https://qiita.com/takasehideki/items/c807f0f364a972271a4c)です！！

楽しみですね:musical_note: