Edited at

ElixirのPipeに関する7のTips


はじめに

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. caseif に値を渡す

Pipe で得られた値は caseif に対しても渡すことが可能であり、 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



参考