ElixirのPipeに関する7のTips

# はじめに
Elixir の Pipe は処理の記述を簡潔にすることができる便利なマクロであり、比較的利用する場面は多いことと思います。
この記事では、そんな Pipe に関する、開発が楽しくなるかもしれないちょっとしたTipsを紹介します。

# 1. `IO.inspect` を挟んで値を出力

Pipe の途中に `IO.inspect` を挟み込む  ことで、処理している途中の値をコンソールに表示させることができます。 `IO.inspect` は与えられた値をそのまま戻り値として返すので Pipe の前後の動作に影響を与えることもなく、デバッグ時に有用な方法と言えるでしょう。


```elixir
defmodule Hoge do
  def do_something do
    10
    |> plus(7)
    |> IO.inspect()
    |> minus(2)
  end

  def plus(x, y), do: x + y

  def minus(x, y), do: x - y
end
```

```
iex(1)> Hoge.do_something()
17
15
```

# 2. `Kernel` で算術演算子を使う

`+` や `-` のような基本的な算術演算子は `Kernel` により提供されています。そのため `Kernel` を明示的に記述することで、これらの演算子を Pipe で使用することができます。

```elixir
defmodule Hoge do
  def do_something do
    10
    |> Kernel.+(10)
    |> Kernel.-(5)
  end
end
```

```
iex(1)> Hoge.do_something()
15
```

# 3. `nil` が  返される場合のデフォルト値をセットする

前述したように `Kernel` は Pipe の中で利用することができますが、 `Kernel.||` を利用すると Pipe の途中で `nil` が返される場合のデフォルト値をセットすることができます。

```elixir
defmodule Hoge do
  def do_something do
    2
    |> double_or_nil()
    |> Kernel.||(0)
    |> Kernel.+(5)
  end

  def double_or_nil(x) do
    if x > 10, do: x * 2, else: nil
  end
end
```

```
iex(18)> Hoge.do_something()
5
```

# 4. `case` や `if` に値を渡す

Pipe で得られた値は `case` や `if` に対しても渡すことが可能であり、 Pipe の途中でそれらのステートメントを利用した処理を記述することができます。

```elixir
defmodule Hoge do
  def do_something do
    50
    |> big_or_small()
    |> case do
      :big -> "big"
      :small -> "small"
    end
    |> Kernel.==("big")
    |> if do
      "This is big!"
    else
      "This is small"
    end
  end

  def big_or_small(x) do
    if 100 < x, do: :big, else: :small
  end
end
```

```
iex(1)> Hoge.do_something
"This is small"
```

# 5. 匿名関数を使う

ちょっとした処理を Pipe の中に挟みたい時は、匿名関数を使うという手段を用いることができます。

```elixir
defmodule Hoge do
  def do_something do
    5
    |> (fn x -> {:ok, x * 3} end).()
    |> (fn {:ok, x} -> x - 10 end).()
  end
end
```

```
iex(1)> Hoge.do_something()
5
```

この方法は、「パイプで処理を繋ぎたいが、第１引数ではなく第２引数に値を渡したい」というようなケースでも有用です。

また、これは `&` を使って記述しても同じことができます。

```elixir
defmodule Hoge do
  def do_something do
    5
    |> (&{:ok, &1 * 3}).()
    |> (&(elem(&1, 1) - 10)).()
  end
end
```

# 6. カスタムした Pipe を追加する

Elixir では、所定の演算子を [オーバーライドして](https://github.com/elixir-lang/elixir/blob/master/lib/elixir/pages/Operators.md#custom-and-overridden-operators) 独自の動作を実装することができます。ここでは、 `~>` をオーバーライドして、渡された値を `IO.inspect` によって出力するパイプを定義してみます。

```elixir
defmodule CustomPipe do
  defmacro left ~> right do
    quote do
      unquote(left)
      |> IO.inspect()
      |> unquote(right)
    end
  end
end

defmodule Hoge do
  import CustomPipe

  def do_something do
    5
    ~> double()
    ~> double()
    ~> double()
    ~> plus(2)
  end

  def double(x), do: x * 2

  def plus(x, y), do: x + y
end
```

```
iex(1)> Hoge.do_something
5
10
20
40
42
```

# 7. `|>` の動きを書き換える

1つ前に紹介した方法では、利用可能な演算子を使って独自の Pipe を定義しましたが、 `|>` そのものの動きを書き換える場合はどうすればいいでしょうか。これは、 `Kernel` を明示的にimportした上で、 `|>` を除外することで実現できます。次の例を見てみましょう。

```elixir
defmodule CustomPipe do
  defmacro left |> right do
    quote do
      unquote(left)
      |> IO.inspect()
      |> unquote(right)
    end
  end
end

defmodule Hoge do
  import Kernel, except: [|>: 2] # exceptを使って |> をimport対象から除外する
  import CustomPipe

  def do_something do
    5
    |> double()
  end

  def double(x), do: x * 2
end
```

※ 上記のようにexceptで除外しない場合、以下のようにコンパイルエラーとなります。

>  function |>/2 imported from both CustomPipe and Kernel, call is ambiguous

# 参考
- [blackode/elixir-tips](https://github.com/blackode/elixir-tips)
- [Two Unique Ways of Piping data in Elixir](https://medium.com/blackode/two-unique-ways-of-piping-data-in-elixir-f0b8766d6df6)
- [Operators](https://github.com/elixir-lang/elixir/blob/master/lib/elixir/pages/Operators.md)