【初心者向け】普段Pythonを書いている人がElixir基礎の中の基礎知識をまとめる

## **はじめに**
はじめてElixirを(3ヵ月だけ)触った時の勉強メモを書いています。初心者向けの内容です。
普段は主にPythonを使っているのでPythonと比較したりします。

## **Elixirとは**
Elixirは`関数型言語`に分類されるプログラミング言語で、Erlangの仮想マシン(BEAM)上で動作します。
[Wikipedia](https://ja.wikipedia.org/wiki/Elixir_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E))

## **基本データ型**
Elixirでは値が不変(immutable)ですが、変数は不変ではありません。

### **真理値**
真理値として小文字で始まる`true`と`false`があります。
`false`と`nil`以外は真とみなされるので、Pythonと違って`0`の場合は`true`になります。

### **アトム(atom)**
名前が自身の値を表わしている定数です。書き方は3つあります。
#### **書き方①**
例： `Hoge`, `Hoge123`, `HogeFuga`

- ルール
  - アルファベットの大文字で始まる
  - 2文字目以降はアルファベット、数字、アンダースコアである
- 用途(慣習)
  - キャメルケースでモジュールの名前を記述する

#### **書き方②**
例： `:hoge123`, `:_hoge`, `:hoge@fuga`, `:hoge?`

- ルール
  - コロンで始まる
  - 2文字目はアルファベットかアンダースコアである
  - 3文字目以降には数字または`@`でもよい
  - 最後の文字は`?`か`!`でもよい
- 用途(慣習)
  - スネークケースでモジュールの名前以外の目的で使用する
  - 例外としてErlangのモジュール名はこの書き方で参照する

#### **書き方③**
例： `:"hoge/fuga"`, `:"hoge@fuga"`

- ルール
  - ダブルクオートで囲んだ文字列の前にコロンを置く

整数、浮動小数、文字列の紹介は [Elixir School](https://elixirschool.com/ja/lessons/basics/basics/#%E5%9F%BA%E6%9C%AC%E3%83%87%E3%83%BC%E3%82%BF%E5%9E%8B) へ。

## **コレクション型**
Elixirのコレクションはどんな型も保持することができます。

### **リスト(List)**
`[1, 2, 3]`

- 特徴
  - 連結データ構造：空か`head`(先頭要素)と`tail`(それ以降の要素)に分ける
  - 順番にアクセスするため、添字での参照やサイズの取得は遅い
  - 先頭に要素を追加するのは速い
- 用途
  - 汎用的なデータ型

#### **リストの関数**
```elixir:headとtail
iex> list = [1, 2, 3, 4, 5]
iex> [head | tail] = list
iex> head
1
iex> tail
[2, 3, 4, 5]
```
```elixir:繰り返し処理
for x <- [1, 2, 3, 4, 5] do 
  IO.puts x 
end
```
```elixir:要素を添え字で参照
list = [1, 2, 3, 4, 5] 
Enum.fetch(list, 1)
#=> 2
```
```elixir:要素を加える
a = [1, 2, 3, 4, 5] 
[0 | a]
#=> [0, 1, 2, 3, 4, 5] 

a = [1, 2, 3, 4, 5] 
[0] ++ a
#=> [0, 1, 2, 3, 4, 5]
```
cons演算子`|`：右辺がリストの場合、左辺の値を右辺のリストの先頭に追加します。

```elixir:要素を結合して文字列を作る
list = [1, 2, 3, 4, 5] 
Enum.join(list)
#=> "12345"
```
Enumモジュールはたくさんの便利関数を提供しています。
https://hexdocs.pm/elixir/Enum.html

### **タプル(Tuple)**
`{:ok, 1}`

- 特徴
  - 要素は順序を持つ
  - 特定の要素アクセスやサイズの取得は速い
  - 要素の追加や変更などは遅い(その度に新しいタプルを作る)
  - リストの`++`演算子や`Enum.each/2`関数にあたるものがタプルに存在しない
  - `for`マクロが使えない
- 用途
  - 特殊な用途のデータ型
  - 関数の戻り値に使用する

### **マップ(Map)**
`%{"hoge" => 1, "fuga" => 2}`

- 特徴
  - キーは順序を持たない
  - 1つのキーが1つの値を持つ
  - キーがアトムの場合は簡易表記ができる
     - `%{:hoge => 1, :fuga => 2}`と`%{fuga: 2, hoge: 1}`は等価
- 用途
  - 汎用的なデータ型

#### **マップの関数**
```elixir:値を取り出す
map = %{"hoge" => 1, "fuga" => 2}
map["hoge"]
#=> 1
 
map = %{:hoge => 1, :fuga => 2}
map.hoge
#=> 1
```
キーがアトムの場合、ドット記法を使うことができます。

```elixir:値を置き換える
map = %{"hoge" => 1, "fuga" => 2}
map = %{map | "hoge" => 2}
#=> %{"fuga" => 2, "hoge" => 2}
 
map = %{"hoge" => 1, "fuga" => 2}
Map.merge(map, %{"hoge" => 2, "piyo" => 3})
#=> %{"fuga" => 2, "hoge" => 2, "piyo" => 3}
```
その他の関数はMapモジュールにあります。
https://hexdocs.pm/elixir/Map.html

### **キーワードリスト(Keyword List)**
`[hoge: 1, fuga: 2]`

- 特徴
  - キーは順序を持つ
  - 同じキーは複数個含まれていても良い(最初のが取得される)
  - キーワードリストの正体は下記3つの条件を満たすリスト
     - すべての要素はタプル
     - タプルの要素数はすべて2
     - タプルの第1要素は常にアトム
- 用途
  - 特殊な用途のデータ型
  - 関数の最後の引数に指定されるのが主な用途(Pythonでいうと`kwargs`)
     - `IO.inspect hoge: 1, fuga: 2`と`IO.inspect [hoge: 1, fuga: 2]`は等価

#### **キーワードリストの関数**
```elixir:値を取り出す
a = [hoge: 1, fuga: 2]
a[:hoge]
#=> 1
```
Mapと違ってドット記法が使えません。

```elixir:先頭に要素を加える
a = [hoge: 1, fuga: 2]
a = [{:piyo, 3} | a]
#=> [piyo: 0, hoge: 1, fuga: 2]
 
a = [hoge: 1, fuga: 2]
a = [piyo: 0] ++ a
#=> [piyo: 0, hoge: 1, fuga: 2]
```
キーワードリストは特殊なリストなので、キーワードリストを操作する場合、リストと同じ方法が使えます。
Keywordモジュールもあります。
https://hexdocs.pm/elixir/Keyword.html

## **モジュールと関数**
### **基本例**
```elixir:モジュールと関数
defmodule Hello do
    def greet(name \\ "world") do
       IO.puts "Hello, #{name}!"
    end
end
 
Hello.greet("Alice")
#=> Hello, Alice!
Hello.greet()
#=> Hello, world!
```
Pythonと違ってモジュールの外では関数を定義できません。

### **関数の戻り値**
関数の中で最後に評価された式の値が関数の戻り値になります。
Python等の`return`に相当する仕組みはありません。

### **アリティ(arity)**
引数を渡さずに`Hello.greet()`で実行すると次のようなエラーメッセージが表示されます。
>** (UndefinedFunctionError) function Hello.greet/0 is undefined or private. Did you mean one of:
>\* greet/1
> Hello.greet()

`Hello.greet/0`は未定義またはプライベートだと言っています。ここの`0`はアリティで、関数の引数を意味します。
`Hello.greet/0`と`Hello.greet/1`は別物です。
※プライベート関数は`defp`で定義します。

### **無名関数**
文字列やアトムなどと同様に「値」の一種です。次のように無名関数を定義して変数`f`にセットします。

```elixir:無名関数
f = fn (a, b) -> a + b end
f.(2, 3)
#=> 5
```

### **パイプライン演算子（|>）**
左辺の関数の戻り値を右辺の関数の第一引数に渡すことができます。
例えば、`Enum.sort/1`と`Enum.reverse/1`でリストをソートして反転する場合、次のようにシンプルな記述ができます。

```elixir:パイプライン演算子
[1,3,2,5,4] |> Enum.sort |> Enum.reverse
#=> [5, 4, 3, 2, 1]
```

### **構造体**
```elixir:構造体の定義
defmodule Player do
  defstruct name: "default", level: 1
#  defstruct [{:name, "default"}, {:level, 1}] も同じ
end
 
player = %Player{name: "hoge", level: 10}
#=> %Player{level: 10, name: "hoge"}
player = %Player{:name => "hoge", :level => 10}
#=> %Player{level: 10, name: "hoge"}
```
`defstruct`は構造体を定義するためのマクロです。構造体のキーとなるアトムのリストを渡します。

```elixir:値を取り出す
player = %Player{name: "hoge", level: 10}
player.name
#=> "hoge"
```
Mapと違って角括弧での取得ができません。

#### マップとの関係
Elixirの構造体とマップはいずれもErlangのマップの拡張として実装されています。構造体は`__struct__`という特別なフィールドを持っています。
Mapの関数は構造体でも使用できます。

```elixir:
player = %Player{name: "hoge", level: 10}
player.__struct__ #=> Player
%Player{} |> is_map
#=> true
```

### **モジュールのディレクティブ**
Elixirには3つのディレクティブが用意されています。

#### *alias*
モジュールのエイリアスを作ることができます。タイピング量を減らす(可読性が上がる)ことが目的です。

```elixir:
defmodule Gstar.Model.Unit.Schema.PlayerUnit do
  alias Gstar.Model.Unit.Master.Unit
  ...
  def assign_units(player_id, unit_ids) do
    Unit.check_unit_id_exists(unit_ids)
  ...
```
別名でエイリアスしたい場合は、`:as`オプションを使います。

```elixir:
defmodule Gstar.Model.Unit.Schema.PlayerUnit do
  alias Gstar.Model.Unit.Master.Unit, as: MasterUnit
  ...
```
複数のモジュールを一度にエイリアスすることもできます。

```elixir:
defmodule Gstar.Model.Unit.Schema.PlayerUnit do
  alias Gstar.Model.Unit.Master.{Unit, Skill}
  ...
```
#### *import*
モジュールの関数やマクロを取り込みたい場合は、`import`を使います。

```elixir:
iex> import Enum
iex> sort([3, 2, 1, 4, 5])
[1, 2, 3, 4, 5]
```
`:only`や`:except`オプションを使うことで、必要なものだけ取り込むことができます。

```elixir:
iex> import Enum, only: [sort: 1]
iex> sum([1, 2, 3])
** (CompileError) iex:12: undefined function sum/1
iex> sort([3, 2, 1, 4, 5])
[1, 2, 3, 4, 5]
```
`:functions`と`:macros`という2つの特別なアトムもあります。

```elixir:
import Enum, only: :functions # 関数だけ取り込む
import Enum, only: :macros # マクロだけ取り込む
```

#### *require*
モジュールで定義したマクロを使う場合は、モジュールを`require`します。

```elixir:
defmodule MyExample do
  require MyMacros
 
  MyMacros.do_stuff
end
```
`require`を含んでいるモジュール(MyExample)をコンパイルする前に指定されたモジュール(MyMacros)のロードが行われるので、マクロ定義が有効になっていることが保証されます。

## **パターンマッチング**
Elixirの開発においてとても大事な概念です。
> パターンマッチング (Pattern matching、パターン照合) とは、データを検索する場合に、特定のパターンが出現するかどうか、またどこに> 出現するかを特定する手法のことである。by wiki

### **マッチ演算子**
`=`は変数への代入を行う演算子ではなく、マッチ演算子と呼ばれて左辺のパターンと右辺の値をマッチさせます。
左辺が変数の場合は、変数に新しい値が代入(束縛)されます。

```elixir:
iex(1)> [a, b, c] = [100, 200, 300]
[100, 200, 300]
iex(2)> [100, 200, 300] = [a, b, c]
[100, 200, 300]
```

```elixir:一致しない場合は例外になる
iex(1)> {a, b, c} = {1, 2}
** (MatchError) no match of right hand side value: {1, 2}
iex(1)> {a, b, c} = [1, 2, 3]
** (MatchError) no match of right hand side value: [1, 2, 3]
```

### **ピン演算子(^)**
一度束縛された変数に新たな値を束縛することができます。再束縛を防ぐにはピン演算子(pin operator)を使います。

```elixir:一致しない場合は例外になる
a = 100
^a = 200
** (MatchError) no match of right hand side value: 200
```

### **アンダースコア変数**
`_`は無名変数と呼ばれる特別な変数です。この変数を参照することはできません。
また、アンダースコアで始まる名前の変数は「使用されない変数」という意味になります。後で参照するとコンパイル時に警告が出ます。
逆にアンダースコアで始まらない名前の変数を後で参照しないと、警告が出ます。

### **パターンマッチングの利用**

```elixir:パターンマッチングによる分岐
case {:ok, "Hello, Alice!"} do
    {:ok, result} -> result
    {:error} -> "Error!"
    _ -> "Catch all"
end
#=> "Hello, Alice!"
```
複数のパターンに対してマッチする必要がある場合、`case/2`を使うことができます。
`_`変数がないと、マッチしない場合にエラーが発生します。

```elixir:パターンマッチングを利用した関数定義
defmodule Hoge do
    def hello(%{name: name}) do
        IO.puts "Hello, #{name}!"
    end
    
    def hello(_) do
        IO.puts "Who are you?"
    end
end
 
Hoge.hello(%{name: "Alice"})
#=> Hello, Alice!
Hoge.hello(%{})
#=> Who are you?
```
引数がマッチするものを特定してその関数を実行します。

### **再帰**
Elixirではループ処理は再帰で表現をします。
例えばリストの値を合計したい場合は次のようにモジュールを定義します。

```elixir:再帰
defmodule Math do
    def sum([]), do: 0
    def sum([head | tail]), do: head + sum(tail)
end
 
IO.puts Math.sum([1, 2, 3, 4, 5])
#=> 15
```
上記のコードは末尾再帰で最適化できます。

```elixir:末尾再帰
defmodule Math do
    def sum(list), do: _sum(list, 0)
    defp _sum([], accumulator), do: accumulator
    defp _sum([head | tail], accumulator), do: _sum(tail, head + accumulator)
end
 
IO.puts Math.sum([1, 2, 3, 4, 5])
#=> 15
```
※この例の場合は`Enum.sum/1`を使うのがベストです。

## **制御構造**
### **if と unless**
```elixir:if
iex> if String.valid?("Hello"), do: "Valid string!", else: "Invalid string."
"Valid string!"
```
Elixirでは`false`と`nil`だけは偽とみなされます。
`unless`は条件が否定される時だけ作用します。

```elixir:unless
iex> unless is_integer("hello"), do: "Not an Int"
"Not an Int"
```
### **case**
[パターンマッチングの利用を参照](#パターンマッチングの利用)

### **cond**
値ではなく、条件をマッチさせたい場合は、`cond`を使うことができます（Pythonでいうと`elif`のようなもの）。

```elixir:cond
iex> cond do
...>   2 + 2 == 5 ->
...>     "This will not be true"
...>   2 * 2 == 3 ->
...>     "Nor this"
...>   1 + 1 == 2 ->
...>     "But this will"
...> end
"But this will"
```
`case`と同様にマッチしない場合にエラーが発生します。`true ->`を定義することで対処できます。

## **範囲(Range)**
`1..10`のように範囲を定義します。内部では構造体として表現されます。
範囲の作成とマッチングの最も一般的なフォームは`Kernel`から自動インポートされる`../2`マクロによってです。

```elixir:範囲のパターンマッチ
iex> range = 1..10
1..10
iex> first .. last = range
1..10
iex> first
1
iex> last
10
iex> range.__struct__
Range
```

## **内包表記(Comprehensions)**
列挙体(Enumerable)をループするための糖衣構文です。

### **ジェネレータ**
```elixir:リスト内包表記
iex> for n <- [1, 2, 3, 4], do: n * n
[1, 4, 9, 16]
```
上記の式での`n <- [1, 2, 3, 4]`がジェネレータです。リスト内包表記で使われる値を生成します。
列挙可能なものなら何でも右側の式に渡すことができます。
パターンマッチングにも対応しています。マッチしない場合、値は無視されます。

```elixir:パターンマッチング
iex> values = [good: 1, good: 2, bad: 3, good: 4]
[good: 1, good: 2, bad: 3, good: 4]
iex> for {:good, n} <- values, do: n * n
[1, 4, 16]
```

入れ子になったループのように、複数のジェネレータを指定することができます。

```elixir:複数のジェネレータ
iex> for n <- [1, 2], m <- [3, 4], do: n * m
[3, 4, 6, 8]
```

### **フィルタ**
必要な値だけを通すのにフィルタを利用することもできます。

```elixir:奇数の二乗だけを取得
iex> require Integer
iex> for n <- 1..4, Integer.is_odd(n), do: n * n
[1, 9]
```
フィルターは`nil`か`false`以外の全ての値を通します。

### **内包表記の返り値**
内包表記は結果としてリストを返します。`:into`オプションを使うことで、リスト以外のデータ構造を生成することができます。
`:into`は`Collectable`プロトコルを実装している構造体を指定できます。

```elixir:オプションの使用
iex> for {k, v} <- [one: 1, two: 2, three: 3], do: {k, v}
[one: 1, two: 2, three: 3]
iex> for {k, v} <- [one: 1, two: 2, three: 3], into: %{}, do: {k, v}
%{one: 1, three: 3, two: 2}
```

### **内包表記のスコープ**
内包表記の内側で代入された変数は、その内包表記の中だけで有効です。外側のスコープの変数に影響しません。

## **まとめ**
Elixirの基礎知識はある程度網羅したつもりですが、ほかにまだまだあります。
書籍を読むなら[『プログラミングElixir』](http://shop.ohmsha.co.jp/shopdetail/000000004675/)は必見です。
それと、[『Elixir/Phoenix 初級』シリーズ](https://books.oiax.jp/)というElixirとフレームワークのPhoenixを紹介する本が分かりやすいので、入門書としては良い選択だと思います。

## **参考サイト**
https://elixirschool.com/ja/
https://elixir-lang.org/getting-started/
https://qiita.com/west-hiroaki/items/553a9d408cc25541e6aa