Help us understand the problem. What is going on with this article?

ElixirのPipeに関する7のTips

More than 1 year has passed since last update.

はじめに

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

参考

Tsuyoshi84
Angular/Vue.jsを用いたフロントエンド開発、Elixir/Phoenixを用いたバックエンド開発、Swiftを用いたiOSアプリ開発などを行なっています。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away