Elixir: パイプ演算子|>について
Tags: elixir, webdev, tutorial, programming
Published: 2019-04-02 02:26:00

---
title: Elixir: パイプ演算子|>について
published: true
description: パイプ演算子|>については、gumi TECH Blog「Elixir入門 10 EnumとStream」の「パイプ演算子」で説明されています。そのおさらいもしつつ、公式ドキュメントの情報などを補います。
tags: #elixir #webdev #tutorial #programming
---

[パイプ演算子`|>`](https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2)については、gumi TECH Blog「[Elixir入門 10: EnumとStream](https://dev.to/gumi/elixir-10-enumstream-4fpb)」の「パイプ演算子」で説明されています。本稿は、そのおさらいもしつつ、公式ドキュメントの情報などを補います。

# パイプライン演算
パイプ演算子`|>`による演算は、パイプライン演算と呼ばれることもあります。左辺の式の値を右辺の関数の第1引数として渡します。

> `左辺 |> 右辺`

たとえば、つぎのコードは`List.flatten/1`でリスト内の入れ子を平坦化します。左辺が第1引数ですので、右辺の関数の引数には加えません。

```elixir
iex> list = [1, [[2], 3]] |> List.flatten 
[1, 2, 3]
```

引数がふたつあるときは、関数には第2引数をかっこ`()`でくくって渡します。この場合のかっこは省けません(省くと警告が示されます)。

```elixir
iex> square = list |> Enum.map(fn(x) -> x * x end)      
[1, 4, 9]
```

引数がふたつ以上あるときも、同じように第1引数を省いて、第2引数以降をかっこ`()`に加えてください。

```elixir
iex> sum = square |> Enum.reduce(0, fn(x, acc) -> x + acc end)
14
```

上記3つの演算をパイプ演算子なしに1行で書くと、つぎのようになります。入れ子になり、しかも内側から演算されますので、読みにくいコードです。

```elixir
iex> sum = Enum.reduce(Enum.map(List.flatten([1, [[2], 3]]), fn(x) -> x * x end), 0, fn(x, acc) -> x + acc end)
14
```

パイプ演算子を使えば、左から演算の順に書けますので、わかりやすいでしょう。

```elixir
iex> [1, [[2], 3]] |> List.flatten |> Enum.map(fn x -> x * x end) |> Enum.reduce(0, fn(x, acc) -> x + acc end) 
```

引数に関数があるときは、キャプチャ演算子`&`を使うとさらに短く書けます(「[Elixir入門 08: モジュールと関数](https://dev.to/gumi/elixir-08--1c4c)」「関数のキャプチャ」参照)。

```elixir
iex> [1, [[2], 3]] |> List.flatten |> Enum.map(&(&1 * &1)) |> Enum.reduce(0, &+/2)
14
```


# パイプ演算子で注意すること
Elixir公式ドキュメントの[`|>/2`](https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2)の項には[ふたつの注意](https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2-pitfalls)が示されています。

## 演算子の優先順位
ひとつは、演算子の優先順位に関わるかっこ`()`づけです。つぎのコードはエラーになります。

```elixir
String.graphemes "Hello" |> Enum.reverse
```

演算子の優先順位にしたがってつぎのように解釈され、[`Enumerable`](https://hexdocs.pm/elixir/Enumerable.html)プロトコルがバイナリに対して定められていないからです。

```elixir
iex> String.graphemes("Hello" |> Enum.reverse)
** (Protocol.UndefinedError) protocol Enumerable not implemented for "Hello"
```

したがって、つぎのように優先順位をかっこで示さなければなりません。

```elixir
iex> String.graphemes("Hello") |> Enum.reverse
["o", "l", "l", "e", "H"]
```

もっとも、せっかくパイプ演算子を使うのですから、つぎのように書くほうがよいでしょう。

```elixir
iex> "Hello" |> String.graphemes |> Enum.reverse
["o", "l", "l", "e", "H"]
```

## 無名関数に用いるとき
もうひとつの注意は、パイプ演算子を無名関数に用いるときです(関数のキャプチャについても同じ)。

```elixir
iex> func = fn(str) -> str |> String.graphemes |> Enum.reverse end
#Function<6.127694169/1 in :erl_eval.expr/5>
```

関数名のあとのドット(`.`)を忘れてはいけませんし、引数がひとつでもかっこ`()`は省けません。

```elixir
iex> "Hello" |> func.()
["o", "l", "l", "e", "H"]
```

けれど、[公式ドキュメントも述べている](https://hexdocs.pm/elixir/Kernel.html#%7C%3E/2-pitfalls)とおり、パイプ演算子を使わない場合でも、ドット(`.`)とかっこ`()`は必要です。うっかり忘れないように、という注意でしょう。

```elixir
iex> func.("Hello")
["o", "l", "l", "e", "H"]
```

少し気をつける点はあるものの、見やすいコードを書くために、パイプ演算子は役立つでしょう。ところで、『[プログラミングElixir](https://shop.ohmsha.co.jp/shopdetail/000000004675/)』(原書『[Programming Elixir 1.6](https://pragprog.com/book/elixir16/programming-elixir-1-6)』)の著者Dave Thomas氏はこのパイプ演算子がお気に入りのようで、書籍のサブタイトルにつぎのように使われています。

> `Functional`
`|> Concurrent`
`|> Pragmatic`
`|> Fun`