テストしやすい「純粋」な関数とは？

## とある日の弊社にて

ハスケル子「やめ太郎さん、今日は何してるんですか？」

ワイ「おお、今日はな」
ワイ「JavaScriptで**お正月判定関数**を書いてんねん」

ハスケル子「お正月判定関数・・・」

ワイ「せや」
ワイ「**株式会社ブラック**はんから、こんな依頼をもらったからな」

> お世話になっております。
> 株式会社ブラックの暗井 暗人（くらい・あんと）です。
> **2020年の元日**に弊社Webサービスのメンテナンスを行いますので、
> 元日の間のみ画面上に「ただいまメンテナンス中です」と表示する機能を実装していただきたいです。
> 予算は800万円までに抑えていただけると幸いです。

ワイ「システムのメンテナンス作業自体は、ブラックはんの方でするみたいなんやけど」
ワイ「**お正月判定関数**だけが**どうしても作られへん**らしく、弊社に依頼が来たという経緯や」

社長「（相変わらず**設定メチャクチャ**やな・・・）」

ハスケル子「ちょっと何言ってるか分からないですけど」
ハスケル子「その**お正月判定関数**とやらは書けたんですか？」

ワイ「おお、書けたで」
ワイ「一ヶ月かけて書いた力作や」
ワイ「ちょっとハスケル子ちゃんも見てみてくれや」

## ワイのお正月判定関数

```js:JavaScript
function shogatsuHantei() {
  const today = dayjs(new Date()).format("YYYY/MM/DD");

  if (today === "2020/01/01") {
    return true;
  } else {
    return false;
  }
}

const isShogatsu = shogatsuHantei();
```

ワイ「↑こんな感じや」

```js:JavaScript
  const today = dayjs(new Date()).format("YYYY/MM/DD");
```

ワイ「↑まずこの部分で、現在の日付を`"2019/12/15"`みたいな文字列に変換して」

```js:JavaScript
  if (today === "2020/01/01") {
    return true;
  } else {
    return false;
  }
```

ワイ「その現在の日付が`"2020/01/01"`と一致していたら`true`を返す」
ワイ「一致していなければ`false`を返す」
ワイ「そんな関数や」

ハスケル子「はぁ」

ワイ「関数自体は書けたから、今から**自動テストを書かなあかん**のや」

> また、Gitコミット時に**自動テスト**が走る設定もよろしくお願いいたします。

ワイ「↑こう暗井はんから頼まれたからな」

ハスケル子「なるほど」
ハスケル子「自動テストいいですよね」

ワイ「せやな」
ワイ「Gitにコミットするときに毎回自動でテストが走るように設定しておけば」
ワイ「今後何かコードの修正をしたときにも」

> 「あ、修正したかった部分はちゃんとできたけど」
> 「〇〇機能のテストが通らんくなっとる！」
> 「今回の修正の影響で、**知らんうちに他の部分をバグらせとった**みたいや！」

ワイ「って場合に、ちゃんと気づけるからな」
ワイ「間違ったことをしていたらコミットすらできない・・・」
ワイ「これが自動テストの大事なところやで！」
ワイ「覚えといてや？ハスケル子ちゃん」


ハスケル子「はぁ」
ハスケル子「でもその**お正月判定関数**」
ハスケル子「**純粋じゃないから自動テストしづらい**ですよ」

ワイ「純粋じゃない・・・？」
ワイ「テストしづらい・・・？」
ワイ「ど、どういうこと・・・？」
ワイ「ワイが**汚れたおっさん**やから、純粋やないってこと・・・？」

ハスケル子「例えば今からテストをするとして」
ハスケル子「今日はお正月じゃないから、`false`が返ってくることは確認できますけど」
> 今日がお正月だったら`true`が返ってくること

ハスケル子「↑これが確認できないじゃないですか」

ワイ「そんなもん**PCの時刻設定をいじって**`2020/01/01`にすればええだけやろ」

ハスケル子「自動テストはどうするんですか」
ハスケル子「Gitコミットしたときに自動テストが走るんですよね？」

ワイ「せ、せや」

> + `2019/12/15`だったら`false`を返す
> + `2020/01/01`だったら`true`を返す
> + `2020/01/15`だったら`false`を返す

ワイ「↑この3種類の自動テストをするように言われとるわ」

ハスケル子「自動テスト中に、どうやってPCの時刻設定を変更するんですか」

ワイ「ぐぬぬ」
ワイ「しゃあないな・・・」
ワイ「自動テスト中に**PCの時刻設定を自動変更**してくれるプログラムを・・・」
ワイ「ハスケル子ちゃん**書いてくれへん？**」

ハスケル子「**書いてくれへん**です」

ワイ「ほな、どうせえ言うん・・・」

ハスケル子「**純粋な関数**に書き換えればいいんですよ」

ワイ「さっきから何なん、その**純粋な関数**って」

ハスケル子「**参照透過な関数**のことです」

ワイ「その参照透過も分からへんわ・・・」

ハスケル子「やめ太郎さんのお正月判定関数は」
ハスケル子「**何月何日に実行するか**によって、**返ってくる値が変わる**じゃないですか」

ワイ「そらそうや」
ワイ「そのための関数やもん」

ハスケル子「そうじゃなくて、**同じ引数**を与えれば**毎回必ず同じ値**を返してくれるのが」
ハスケル子「**参照透過な関数**です」

ワイ「ぐ、具体的には」
ワイ「さっきの関数をどう変えればいいの・・・？」

ハスケル子「説明しますね」

## 純粋な関数に書き換え

ハスケル子「さっきのやめ太郎さんの関数だと」
ハスケル子「**関数の中で**現在の年月日を取得していましたけど」
ハスケル子「それを**関数の外**に出します」

```js:JavaScript
const today = dayjs(new Date()).format("YYYY/MM/DD");

function shogatsuHantei() {
  // 省略
}
```

ハスケル子「↑こうですね」
ハスケル子「そして、その`today`は」
ハスケル子「お正月判定関数の**引数**として渡すようにします」

```js:JavaScript
const today = dayjs(new Date()).format("YYYY/MM/DD");

function shogatsuHantei(today) {
  if (today === "2020/01/01") {
    return true;
  } else {
    return false;
  }
}

const isShogatsu = shogatsuHantei(today); // 引数として渡す。
```

ハスケル子「↑こうですね」

ワイ「へぇ・・・」

ハスケル子「こうすることで、今日の日付だけじゃなく」
ハスケル子「引数として**任意の日付**を渡して実行することもできるようになったので」
ハスケル子「テストの要件である**3パターンの日付**も簡単に試せます」

```js:JavaScript
shogatsuHantei("2019/12/15"); // false
shogatsuHantei("2020/01/01"); // true
shogatsuHantei("2020/01/15"); // false
```

ワイ「ほんまや・・・」

ハスケル子「現在の年月日という**状態**に依存しなくなって」
ハスケル子「**引数によってのみ**結果が変わる**純粋な関数**になりました」
ハスケル子「つまり**引数の渡し方**で**戻り値をコントロールできる**ようになったので」
ハスケル子「自動テストも簡単に複数パターン試せるわけです」

ワイ「なるほどな・・・！」
ワイ「関数の純粋性・・・！」

ハスケル子「PCの時刻設定を自動で変更するバッチファイルとかも」
ハスケル子「書けなくはないですけど」
ハスケル子「もしバグが出た場合に」
ハスケル子「そのバッチファイルが間違っているのか」
ハスケル子「関数自体が間違っているのか」
ハスケル子「って感じで」
ハスケル子「懸念するポイントが増えてしまうじゃないですか」

ワイ「せやな・・・」

ハスケル子「そんな感じで」
ハスケル子「周囲の状態に依存しない**純粋な関数**は」
ハスケル子「**自動テストとの相性が良い**んです」
ハスケル子「数ある言語の中には、純粋な関数しか書けない**純粋関数型言語**なんてのもあるんですよ」

ワイ「おお、初めから純粋にしか書けなければ」
ワイ「ワイみたいにうっかり状態依存の関数を書いてしまうこともないもんな」
ワイ「テストする段階になって気づいて」

> 「この関数、書き換えなアカンやん！」

ワイ「なんてことがなくなるんか」
ワイ「ええやん」
ワイ「**純粋関数型言語**って、例えばどんなんがあるん？」

ハスケル子「フロントエンドの言語だと、例えば**Elm**です」

ワイ「おお、『実用上[**実行時エラーが出ない**](https://qiita.com/Yametaro/items/5a8474523d7ba3a3a3e4)』と言うあの言語か・・・！」
ワイ「ワイもこのあと勉強してみるわ！！！」

ハスケル子「[**公式ガイドの日本語訳**](https://guide.elm-lang.jp/)が読みやすくてオススメですよ」

ワイ「おお、ありがとう！」
ワイ「ほな、また明日な！」

**そしてやめ太郎は、飲み屋街へと消えていった・・・**

## 翌日
ハスケル子「昨日ちゃんとElmの勉強しましたか？」

ワイ「ああ、一応酒を飲みながらElmのこと勉強したんやけど」
ワイ「かなり飲んだから何もかも忘れてもうたわ」
ワイ「やっぱ、酒飲みながら勉強しても得るものないな」
ワイ「Elmやなくて**得る無**やわ！」
ワイ「ガーファファファ！」

ハスケル子「・・・」
ハスケル子「（**純粋にイラつく・・・**）」

**〜おしまい〜**