関数とは何か
まずプログラミングにおける関数とは何かということを、はっきりさせておきたい。
私は関数を「外部から何かを受け取り、加工して返すもの」と認識している。
たとえば平方根を返す関数Sqrなどは、2を受け取るとルート2の値(1.4142135623731)を返す。
しかし、Microsoftのヘルプには、この定義に当てはまらないものもある。
たとえばNow関数である。
Now関数は、FunctionではなくPropertyとして定義されている。
thom.hateblo.jp
そして、Now関数は値を受け取らず、ただ日時を返すのみである。
まあ、「無」を受け取ると考えれば定義には合致する。
とにかく関数は、値を返すものである。これに例外は無い。
MsgBox関数はどうか。
Sub Test() MsgBox "Hello, VBA" End Sub
何も返していないように見えるが、実はMsgBox関数は1を返している。
(厳密にはVbMsgBoxResult列挙型のvbOK定数値が1ということ)
Testプロシージャが帰ってきた値を使っていないだけで、MsgBox関数自体はちゃんと値を返しているのだ。
次のように書き換えれば、意味が分かるだろう。
Sub Test() Debug.Print MsgBox("Hello, VBA") End Sub
関数の副作用とは
関数の主作用は、値を返すことである。
MsgBoxなどの表示を主目的とする関数であっても、「関数」という定義に基づけば値を返すことが主作用なのである。
ではMsgBoxの副作用とは何かというと、メッセージを表示することである。
副作用というと薬の副作用を連想して悪いものをイメージしてしまうが、プログラミングにおいて必ずしも悪いものではない。
しかし、副作用が思わぬバグをもたらすこともある。
たとえば、以下のような関数を作成してみた。
Function CellTest(x, y) Range("a1").Value = x Range("a2").Value = y CellTest = Range("a1").Value * Range("a2").Value End Function
xとyを受け取り、セルに書き込んだ上で掛け合わせた値を返す関数である。
セルへの書き込みは副作用である。
もしシートにロックがかかっていたら?ActiveSheetがグラフシートだったら?
他にも色々弊害が考えられる。上記のコードは、あまり良いコードとは言えない。
関数の参照透過性とは
参照透過性とは、外部状態に影響を受けず、与えられた値のみによって結果が決まる性質のことをいう。
たとえばNow関数は参照透過ではない。
外部環境であるシステム時刻によって、返す値が変わるためだ。
一方、平方根を返すSqrは参照透過である。
誰が、いつ、どのような状況で呼び出そうとも、2を与えれば必ず1.4142135623731を返す。
透過というのは、文字通り、透けて見えるということである。
内部のごちゃごちゃした計算が透けて見える。
たとえば、Sqr関数は平方根を返すということだけ知っていれば、内部の計算はまったく意識しなくて良いということである。
まとめ
本来関数とは外部の状況に左右されず、与えられた値のみによって結果が決まるものである。
つまり、副作用が無く、参照透過な関数こそが、純粋な関数といえる。
そうした関数は、内部の処理を意識しなくて良いので非常に使いやすい。
自分でFunctionプロシージャーを作るときも、なるべく純粋な関数を意識すると良い。
外部環境に依存するような処理はSubプロシージャで行い、関数内では与えられた引数のみで完結させるのが良い設計である。
ただし、InputBox関数、Now関数、MsgBox関数のように、副作用の恩恵によって成り立つ関数は多数ある。
私が伝えいのは「闇雲に副作用を作るべきではない」ということであり、時と場合によっては、あえて参照透過性を犠牲にして副作用を作ることもある。
要は、こうした基本を踏まえたうえでコーディングすると良いということ。