isyumi_netブログ

isyumi_netがプログラミングのこととかを書くブログ

純粋関数の割合を最大化しよう

不勉強な僕ですが、僕が知っているいくつかの小手先のテクニックをまとめておこうと思う。

誤解しないで欲しいが僕は他人にこの話をすることはほとんど無い。僕があなたの同僚になったとして、いちいち突っ込んでめんどくさい思いをさせることはない。

単にあなたの人生の一助になればという思いである。

 

というわけで第一弾は純粋関数の割合を最大化しよう、である。

純粋ではない関数には二種類ある

 

ひとつ目は関数の外側の状態に影響を受ける関数だ。

var value = 10;

int main(int mul) {

  return value * mul;

}

このmain関数は関数の外側にあるvalueという変数を読み取っている。この関数を何回も実行した場合、引数が毎回同じでもvalueの値が違えば違う結果が返る。

 

もうひとつは関数の外の変数を書き換える関数だ。 

var value = 10;

int main(int mul) {

  value = mul;

  return mul * 2; 

}

 

このmain関数は関数の外にあるvalue変数を書き換えている。

 

このように純粋ではない関数には2種類あることを見てきた。 

逆に純粋な関数とは引数が同じなら毎回同じ値を返すし、関数の外の世界に影響を及ぼさない関数だ。

ちなみに引数が同じだったら必ず同じ結果を返すことを「引数に対して決定的である」と言い回すらしい。

 

void main(int value , int mul) {

  return value * int;

}

 

こういう関数の利点として

  • 気軽に呼び出しやすい
  • テストしやすい

が挙げられる

 

気軽に呼び出しやすいとは、この関数を最初に意図した以外の目的に使う場合に影響を調査しなくていいということだ。

例えば、「画面に〇〇を計算した結果を表示してほしい」と頼まれたとしよう。

〇〇を計算する関数はすでに定義済だった。

もし、この関数が別の場所に定義された変数によって結果が変わったり、関数を実行するたびにDBをUpdateしたりするなら、そのまま使えない。

しかし、純粋関数であることが明白なら、気軽に呼び出して使うことができる。

つまり、純粋関数は便利だということだ。

 

ところが、純粋関数だけで何かの役に立つプログラムを書くことはできない。

例えば関数の外にある値として

  • DB
  • 現在時刻
  • 乱数

が挙げられる。

また、関数の外の値を書き換えることとして

  • HTTPレスポンス
  • DBに保存

などが挙げられる。

通常このような機能を一切持たないプログラムを書くことはない。

どうしてもひとつのプログラムには純粋関数とそうではない関数が交じり合うことになる。

 

そこで、純粋関数の割合を最大化することを目指すべきだ。

下のGistをご覧頂きたい。

 

純粋関数の割合を最大化

 

originalとgoodは結果的に同じことが起こるコードだ。

method1からmethod2を呼び出し、更にmethod3を呼び出している。間に挟まっているhoge関数は適当ななんか難しい処理だと思っていたきたい。

 

originalの方ではmethod3で現在時刻を取得し、DBへ値の書き込みをしている。

 

method3は純粋関数ではない。そして純粋ではない関数を呼んでいるmethod2とmethod1も自動で純粋関数でなくなる。これまでのところ純粋:非純粋の比率は0:3である。

 

しかし、下のgoodの方では、

  • 現在時刻の取得とDB書き込みを一番浅い場所に持ってきた
  • 深い場所にあるメソッドは自分で現在時刻を取得するのでなく引数で受け取っている
  • 深い場所にあるメソッドは自分でDBに保存するのでなく保存する値を戻り値で帰す

という書き換えを行った。結果、method2とmethod3は純粋関数にすることができた。

 

method1を見捨ててmethod2とmethod3を救うことができた。

純粋:非純粋の比率は2:1になり大逆転勝利を収めることができた。

 

 

結論

純粋関数の割合が最大化するように書き換えよう。

 

 注釈

用語の正確な定義について自身がなかった。

もともと僕は

関数の外部の変数を書き換えること:副作用

関数の外部の変数に依存すること:参照透過性がない

副作用がなく、かつ参照透過な関数:純粋関数

と思っていた。

しかし、Wikipediaのこの記述を見るとどうもそうじゃないらしい。

副作用 (プログラム) - Wikipedia

同じ条件なら必ず同じ結果を返す、かつ、外に影響を及ぼさないことを参照透過と呼ぶということかな。

参照透過な処理は副作用から独立しているというのは、「他の処理に副作用を及ぼさないし他の処理の副作用に影響を受けないよ」っていう意味か。

ちょっと良くわからなかった。

じゃあ他の処理に影響を与えるけど引数に対して決定的な処理はなんて呼ぶのだ。

逆に他の処理の影響を受けるけど他の処理に影響を与えない処理はなんて呼ぶのだ。

まさかりは怖いけど枕投げくらいならWanted。