Elixirで面食らった演算子・記法まとめ

### はじめに

7月くらいから業務でElixirを書くようになり、見たことも書いたこともない状態からなんとか見よう見まねでひと通りのことは書けるようになってきました。

Elixirを書き始めて面食らった演算子や記法が多かったので、ここにまとめてみようと思います。なお筆者は7月以前主にrubyを書いていたのでrubyとの比較例が多いです。

なお、こちらのエントリに記載しているコンソール上での例はElixir 1.3.0で動作させています。

### 演算子編

#### パイプ演算子

```elixir
iex(1)> sample = [1, 2, 3]
[1, 2, 3]
iex(2)> Enum.count(sample)
3
iex(3)> sample |> Enum.count
3
```

`|>` の左側に記述した変数が、右側の関数の第一引数として渡されます。

例えば、あるパラメータ `params` から

* `A module` で必要なデータを取得
* `B module` で取得したデータをフィルタリング
* `C module` でフィルタリングしたデータを整形する

といった処理を行う時

rubyでは

```ruby
result = C.format(B.filter(A.fetch(params)))
```

ないし

```ruby
fetched_data = A.fetch(params)
filtered_data = B.filter(fetched_data)
formatted_data = C.format(filtered_data)
```

のように記述するかと思います。
elixirでも、パイプ演算子を利用しない場合には

```elixir
result = C.format(B.filter(A.fetch(params)))
```

と処理を行う順番とは逆順に左側から利用するmethodを記述することになります。

これをパイプ演算子を利用すると

```elixir
result = params
|> A.fetch
|> B.filter
|> C.format
```

のように記述することが出来ます。

パイプ演算子を利用すると、処理の順番通りに記述することができ直感的に `params` の操作を理解することが出来ますね。

#### マッチ演算子

パイプ演算子の例でサラッと書いてしまいましたが、 Elixir では `=` はマッチ演算子として使われています。
この演算子の左側に記述された変数に、右側に記述された内容を代入するようになっています。

簡単な例は以下です。

```elixir
iex(4)> sample = 1
1
iex(5)> 1 = sample
1
iex(6)> 2 = sample
** (MatchError) no match of right hand side value: 1
```

最後の行で、2にsampleをマッチさせようとして `MatchError` がraiseされているのが分かります。

また、マッチ演算子の強力な所は、右辺に記述されたコレクションの構造を利用して以下のように複数の変数に代入することが出来る点です。

```elixir
iex(7)> sample = [1, 2, 3]
[1, 2, 3]
iex(8)> [element1, element2, element3] = sample
[1, 2, 3]
iex(9)> element1
1
iex(10)> element2
2
iex(11)> element3
3
```

```elixir
iex(1)> map = %{key1: "value1", key2: "value2"}
%{key1: "value1", key2: "value2"}
iex(2)> %{key1: value1, key2: value2} = map
%{key1: "value1", key2: "value2"}
iex(3)> value1
"value1"
iex(4)> value2
"value2"
```

rubyでは同じような書き方では複数にマッチさせて代入するという書き方がHashの場合はできません。

```ruby
irb(main):008:0> sample = [1, 2, 3]
=> [1, 2, 3]
irb(main):009:0> (element1, element2, element3) = sample
=> [1, 2, 3]
irb(main):010:0> element1
=> 1
irb(main):011:0> element2
=> 2
irb(main):012:0> element3
=> 3
```

```ruby
irb(main):013:0> hash = {key1: "value1", key2: "value2"}
=> {:key1=>"value1", :key2=>"value2"}
irb(main):014:0> {key1: value1, key2: value2} = hash
SyntaxError: (irb):14: syntax error, unexpected '=', expecting end-of-input
{key1: value1, key2: value2} = hash
```

#### ピン演算子

マッチ演算子の左辺に置いた変数に対し、右辺に記述した内容で代入せず固定しておきたい場合に使います。

```elixir
iex(14)> sample = 1
1
iex(15)> ^sample = 2
** (MatchError) no match of right hand side value: 2
```

色々なリファレンスでは、このような例が多いですが実際の業務では余り利用しませんでした…

`Ecto.Query` というクエリを扱うmoduleでクエリを書く時に出てくる `^` の記法ですが、[リファレンス](https://hexdocs.pm/ecto/Ecto.Query.html#module-interpolation-and-casting) を読むと

> External values and Elixir expressions can be injected into a query expression with ^

ということなので、外部の変数やElixirの式をクエリ内で展開する場合は `^` で囲んでおきましょうというところですね。

ライブラリの内部実装を確認したところ([gist](https://gist.github.com/manji602/5b8cf30ed3bf55f5bfd9de380c760a38))、この実装はピン演算子として処理されているものと思われます。

```elixir
iex(23)>  User |> where([user], user.id == ^user_id)
#Ecto.Query<from s in User, where: s.id == ^1>
iex(24)>  User |> where([user], user.id == ^(1 + 2))
#Ecto.Query<from s in User, where: s.id == ^3>
```

### 記法編

#### コレクション

**Map**

キーが `Atom` であれば、ドット記法を使うことが出来ます。

```elixir
iex(1)> map = %{key1: "value1", key2: "value2"}
%{key1: "value1", key2: "value2"}
iex(2)> map[:key1]
"value1"
iex(3)> map.key1
"value1"
iex(4)>
```

**List**

`head` , `tail` でそれぞれ「先頭の要素」「先頭以外の要素」を取得することが出来ます。

```elixir
iex(4)> list = [1, 2, 3]
[1, 2, 3]
iex(5)> [head | tail] = list
[1, 2, 3]
iex(6)> head
1
iex(7)> tail
[2, 3]
```

このあたりはLispのcar, cdrと似ているので触ったことのある方は概念の理解がしやすいと思います。

```lisp
(car '(1 2 3 4))
=>1

(cdr '(1 2 3 4))
=>(2 3 4)
```

#### 無名関数

```elixir
iex(27)> fn(a, b) -> a + b end
#Function<12.52032458/2 in :erl_eval.expr/5>
```

無名関数は変数にマッチさせることが出来、これを利用して呼ぶことが出来ます。呼び出すときには `.` を付けて呼ぶことに注意です。

```elixir
iex(28)> sum = fn(a, b) -> a + b end
#Function<12.52032458/2 in :erl_eval.expr/5>
iex(30)> sum.(1, 2)
3
```

また`fn -> end` までのくくりを `&()`、第一引数・第二引数…を `&1, &2, ...` として簡略化することが出来ます。

```elixir
iex(32)> sum = &(&1 + &2)
&:erlang.+/2
iex(33)> sum.(1, 2)
3
```

#### alias

あるmoduleから別のmoduleのメソッドを利用する場合に、いくつか依存を宣言する記述の仕方がありますが、 `alias` はmoduleに別名をつけることが出来ます。

```elixir
defmodule Hoge.Huga.A do

  def do_something do
    Hoge.Huga.B.some_method
  end
end
```

通常上のように記述するところを、 `alias` を利用すると以下のようにシンプルに書けます。

```elixir
defmodule Hoge.Huga.A do

  alias Hoge.Huga.B

  def do_something do
    B.some_method
  end
end
```

また、 `as:` オプションを利用すると、オプションで指定したmodule名で呼ぶことが出来ます。

```elixir
defmodule Hoge.Huga.A do

  alias Hoge.Huga.B, as: C

  def do_something do
    C.some_method
  end
end
```

#### 関数のガード節

引数に条件を付けて、同じ関数名で別の処理を書くことが出来ます。

```elixir
defmodule Sample do
  def func1(x) when is_number(x) do
    IO.puts "#{x} is a number"
  end

  def func1(x) when is_atom(x) do
    IO.puts "#{x} is an atom"
  end
end
```

#### with式

処理の過程で一時的な変数が必要になった場合に、スコープを限定する場合に利用します。
下記の例では `params` という変数が2回登場していますが、with式内に定義されたものはそのスコープ外では参照できないようになっています。

```
iex(8)> params = %{hoge: "huga"}
%{hoge: "huga"}
iex(9)> result = with params = %{another_hoge: "another_huga"} do "#{params[:another_hoge]}" end
"another_huga"
iex(10)> params
%{hoge: "huga"}
```

#### 要素の結合

配列の結合は `++` で記述します。

```elixir
iex(18)> array1 = [1, 2, 3]
[1, 2, 3]
iex(19)> array2 = [4, 5, 6]
[4, 5, 6]
iex(20)> array1 ++ array2
[1, 2, 3, 4, 5, 6]
iex(21)> array1 + array2
** (ArithmeticError) bad argument in arithmetic expression
    :erlang.+([1, 2, 3], [4, 5, 6])
```

文字列の結合は `<>` で記述します。

```elixir
iex(21)> string1 = "hoge"
"hoge"
iex(22)> string2 = "huga"
"huga"
iex(23)> string1 <> string2
"hogehuga"
iex(24)> string1 + string2
** (ArithmeticError) bad argument in arithmetic expression
    :erlang.+("hoge", "huga")
```

数値の場合は `+` で記述します。

```
iex(31)> 1 + 2
3
```

rubyの場合は全て `+` で記述していたので、これに慣れているともどかしいところです。。

### おわりに

Elixirの演算子や記法は、他の言語であまり見られないものが色々とあり、初学の状態ではコードの内容を理解するのに苦労したためこのエントリを書いてみました。これからElixirを触られる方の一助になれば幸いです :relaxed: