はじめに
Elixir の Pipe は処理の記述を簡潔にすることができる便利なマクロであり、比較的利用する場面は多いことと思います。
この記事では、そんな Pipe に関する、開発が楽しくなるかもしれないちょっとしたTipsを紹介します。
1. IO.inspect
を挟んで値を出力
Pipe の途中に IO.inspect
を挟み込む ことで、処理している途中の値をコンソールに表示させることができます。 IO.inspect
は与えられた値をそのまま戻り値として返すので Pipe の前後の動作に影響を与えることもなく、デバッグ時に有用な方法と言えるでしょう。
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 で使用することができます。
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
が返される場合のデフォルト値をセットすることができます。
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 の途中でそれらのステートメントを利用した処理を記述することができます。
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 の中に挟みたい時は、匿名関数を使うという手段を用いることができます。
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
この方法は、「パイプで処理を繋ぎたいが、第1引数ではなく第2引数に値を渡したい」というようなケースでも有用です。
また、これは &
を使って記述しても同じことができます。
defmodule Hoge do
def do_something do
5
|> (&{:ok, &1 * 3}).()
|> (&(elem(&1, 1) - 10)).()
end
end
6. カスタムした Pipe を追加する
Elixir では、所定の演算子を オーバーライドして 独自の動作を実装することができます。ここでは、 ~>
をオーバーライドして、渡された値を IO.inspect
によって出力するパイプを定義してみます。
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した上で、 |>
を除外することで実現できます。次の例を見てみましょう。
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