Elixir始めたてだった頃からの書き方の変遷

# はじめに
こんにちは。12/13の[Elixir Advent Calendar](https://qiita.com/advent-calendar/2021/elixir)を書かせていただきます。

最初は面白いライブラリとか（docs周り）について書こうと思っていたのですが、僕のコードの書き方の変遷について書くことにしました。
最初のころは「パイプライン演算子のある、なんかイケてる関数型言語」っていうイメージと、[クラロワ](https://clashroyale.com/ja/)をして当時よく遊んでいたので、馴染みのある**エリクサー**という単語に興味を引かれて始めました。それまでパターンマッチや再帰を使いこなせたことがなかった僕にとって、Elixir風の書き方を試していくことはとても斬新な感じがして楽しく思いました。
最初から上手には全然書けていないし、今でも直せそうなところはたくさんあると思いますが、これを読んでElixirに興味を持ってくれる方が一人でも増えたら嬉しいです。

それでは、前の自分を思い出しながら書いていきます。

# よくわかっていない期
**はじめに**の理由から、Elixirをやってみようと思いました。
「〇〇が作りたい」だったり、「関数型言語にちょっとでも触れてみたい」というわけではなく、**エリクサー**という単語に馴染みがあったのが大きかったです。

Elixirの文法をちょろっとやった後、何か動くものを作ってみようと思い、Phoenixを使ってチャットアプリケーションを作りました。当時の僕はまだ公式ドキュメントをちゃんと読んでいくことが出来なかったので、どこかの記事を参考にして作成しました。

次のようなコードを書いたりもしていました。

```elixir
def search_id(conn, _) do
  result = conn.params["word"]
    |> Auth.search_id()

  json(conn, formatuser(result))
end
```

当時の僕の、とりあえずパイプライン演算子が使ってみたかったという気持ちが感じられます。
「後続の関数の第一引数に値を渡す」のがパイプライン演算子の役割でしたが、`Enum`モジュールやリストについての理解が浅かった当時は、使いみちがよくわかっていませんでした。

他にもよくわかっていなかった概念はすごくたくさんありました。
- チェンジセット
- チャンネル
- スキーマ（マイグレーションファイルもあるのになんでだろう？って思ってました）
- ピン演算子
- use require import（aliasはわかった）
- conn
- パターンマッチ
- with

HexDocsが理解できないので、Elixir Schoolや、これらのわからないところの説明をしてくれているサイトでちょっとずつぼんやりとイメージをつけていきました。（理解したとは言っていない）
そしてパイプライン演算子ってもっといい感じに使えないのかな、と考えていった結果、`Enum`モジュールと出会いました。
`Enum`の登場によって、僕のElixir理解は非常に深まりました。

# Enum大好き期
この時期は、`Enum`モジュールが使いたくて使いたくて仕方がありませんでした。
`Enum`を使うとパイプライン演算子でつなげてリストに色々なことを行えます。


```elixir
names = User
  |> where([u], u.id == ^user_id)
  |> Repo.all()
  |> Enum.map(fn user ->
    user.name
  end)
```

このサンプルプログラムからもイメージしやすいように、データを次々と扱いやすいように変換したりする際は`Enum`とパイプライン演算子の相性がとても良いです。
`Enum`モジュールは今でもすごく気に入っていて、リストを扱う際にすごく頻繁に利用しています。
しかし、このときは`Enum`とパイプライン演算子の相性の良さにただただ驚き、使えそうな場面では必ずと言っていいほど`Enum`を使っていました。

`Repo.all`と`Enum`があればなんだってできる。
強大な力を手にしてしまったこの頃の自分はすべてのデータをパイプラインでつなぎたい衝動に駆られていくようになりました。
Enumを覚えていくと同時に、体がパイプラインの快感を求めていくようになってしまいました。

そんな状況で、自分の中に一つの疑問が浮かびました。
「パイプラインの中にKernelの演算子を入れてしまってもいいのだろうか？」

```elixir
User
|> where([u], u.id == ^user_id)
|> Repo.all()
|> length()
|> Kernel.==(10)
|> assert()
```
例えばこのような感じです。これが許されるのなら、もう何者も僕のパイプラインを妨げることは出来ません。


# パイプライン信仰期
多少の関数分けはしつつも、すべての関数を**パイプラインでつなげるために**作ってしまう時期に突入しました。
**このころのコードが一番見づらかったです。**
お見せできるプログラムがもう残っていないのですが、思い出して書いてみるとこんな感じです。

```elixir
User
|> where([u], u.id == ^user_id)
|> Repo.all()
|> length()
|> Kernel.==(10)
|> if do
  Company
  |> where([c], c.id == ^company_id)
  |> Repo.all()
  |> Enum.map(&Map.get(&1, :name))
  |> Enum.reject(&is_nil(&1))
else
  []
end
```

この頃に`if`や`case`にパイプライン演算子で値を提供できることを知り、衝撃を受けました。
パイプラインでつなげるためにそれらの条件分岐を多用し、条件分岐のために`boolean`を返すような処理をパイプラインの中に挟んでいました。
また、ここでは書いていませんが、`~>`演算子を定義して、パイプライン最中での変数束縛も可能にしていました。
> 参考
> https://www.reddit.com/r/elixir/comments/7j2lx2/pipe_a_functions_output_into_a_variable/

この長い長いパイプラインを書いているときは、まるでぷよぷよの連鎖のように処理が繋がっていくので、非常に気持ちが良かったです。特に、テストをするために`〇〇Test`モジュールで、巨大なパイプライン連鎖を生み出しまくっていました。

そんなある日、久しぶりに[リーダブルコード](https://www.amazon.co.jp/%E3%83%AA%E3%83%BC%E3%83%80%E3%83%96%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89-%E2%80%95%E3%82%88%E3%82%8A%E8%89%AF%E3%81%84%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%8F%E3%81%9F%E3%82%81%E3%81%AE%E3%82%B7%E3%83%B3%E3%83%97%E3%83%AB%E3%81%A7%E5%AE%9F%E8%B7%B5%E7%9A%84%E3%81%AA%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-Theory-practice-Boswell/dp/4873115655)を読み返してみたら、次のように書いてありました。

> 「読みやすい」コードを書くのに「面白い」ことをする必要はない。

これは今の自分に最も欠けている感覚だ！
パイプライン演算子に取り憑かれてしまっていた自分を反省して、自分がそれまでに生み出してきた「書くときは気持ちいいけどメンテナンスのしづらいコード」を1つずつ直していくことにしました。
やはりプログラムは読みやすくなくては。

# パターンマッチ・defp期
どうすれば読みやすいコードが書けるのかを知るために、Githubで他の人のコードを色々と見たりしました。
この頃はもう始めたての頃と比べると格段にコードを追えるようになっていたので、読んでいく上での苦労は最初よりも少なくなっていました。
他人のコードを読むことは、自分にとって非常に勉強になりました。中には`with`をスッキリと書くために[OK](https://hexdocs.pm/ok/0.2.0/readme.html)というライブラリを入れている人がいたり、ちゃんとケースに応じて`Kernel`の関数をパイプラインの中に組み込んでいる人がいたりしたことが印象に残っています。

ここで僕は新しいことをいくつか知りました。

- エラーハンドリングのしやすいコードを書こう
- パターンマッチを積極的に使ってネストを減らそう
- defp関数を**適切に**使って関数分けをしよう
- よく使う構造のマップは、構造体として定義して名前を付けてあげよう
- etc

リーダブルコードに救われました。

# 現在
現在も、過去のパイプライン狂だった自分を反省して読みやすいコードを心がけて書いています。

```elixir
  @doc """
  Check if the given instance name exists.
  """
  def instance_exists?(instance_name, db_index) do
    conn = conn()

    with {:ok, _}   <- Redix.command(conn, ["SELECT", db_index]),
         {:ok, val} <- Redix.command(conn, ["GET", instance_string_key(instance_name)]) do
      !is_nil(val)
    else
      _ -> false
    end
  end
```

今の自分の課題は

- しっかりとエラーハンドリングのできるコードを書くこと。
- コードを追いやすく、イメージもしやすいモジュール依存関係で書いていくこと。
- 適切な命名をすること。

です。パターンマッチや`with`自体を覚えることは決して難しくありませんが、これらの**メンテナンス性**に関わるような部分はちゃんと毎回意識して書かないと上達していかないなと思い、よくリファクタリングをしています。

# おわりに
パイプライン演算子を濫用せずとも、Elixirは楽しく書いていくことができる言語です。したがって、面白くなくて読みやすいコードしか書いちゃいけなかったとしても、嫌にならずに学習を続けていくことができます。
Web開発をされている・興味があるみなさん、是非この楽しいElixirという言語に触れてみてほしいです。
拙い文章でしたが、読んでいただいてありがとうございました。