関数型プログラミング入門:なぜ「参照透過性」が重要なのか?

こんにちは。ゆうせいです。

オブジェクト指向の研修も一息ついた頃でしょうか?新人研修でクラスやメソッドについて学んだ後、次に「関数型プログラミング」という言葉を耳にすることがあるかもしれません。

「オブジェクト指向と何が違うの?」「なんだか数学的で難しそう…」

そんなふうに感じていませんか?

今日は、その「関数型プログラミング」という考え方の「心臓部」とも言える、参照透過性(さんしょうとうかせい)という重要な概念について、一緒に学んでいきましょう。

この言葉さえ押さえてしまえば、関数型プログラミングが目指している世界が、きっと見えてきますよ!


参照透過性って、いったい何?

まず、この「参照透過性」という、ちょっと厳つい名前の概念から解説しますね。

これは、一言でいうと「計算結果がいつ、どこでやっても絶対に変わらない」という性質のことです。

数学の世界は「参照透過」

一番わかりやすい例は、数学の計算です。

例えば、「$10 + 5$」という式を考えてみてください。

この答えは、もちろん「$15$」ですよね。

この計算は、昨日やっても、今日やっても、10年後に地球の裏側でやっても、絶対に「$15$」です。

「$10 + 5$」を計算したせいで、あなたの体力が回復したり、ゲームのセーブデータが書き換わったりすることも、絶対にありません。

プログラムの世界で言うと、

  1. 同じ材料(引数)を渡せば、必ず同じ結果(戻り値)が返ってくる。
  2. 計算の途中で、外部の世界に一切影響を与えない(専門用語で「副作用がない」と言います)。

この2つの鉄のルールを守っている関数や式を、「参照透過性がある」と呼びます。

まさに、数学の関数のようですよね。


関数型プログラミングは「参照透過性」が大好き

では、本題の「関数型プログラミング(Functional Programming)」とは何でしょうか?

これは、プログラムを組み立てる「考え方」や「スタイル」の一つです。

そして、関数型プログラミングは、この「参照透過性」を非常に大切にします。

できる限り、プログラムを「参照透過性がある」関数(純粋関数と呼びます)の組み合わせだけで作ろう!というのが、関数型プログラミングの基本的なアプローチなんです。

「え、でもプログラムって、データを書き換えたり(HPを減らしたり)、ファイルに保存したり(副作用)するものじゃないの?」

そう思いますよね。そこが、オブジェクト指向との大きな違いです。

特徴1:データは「不変(Immutability)」

関数型プログラミングの大きな特徴に、「データは原則として変更しない(不変)」というルールがあります。

オブジェクト指向では、プレイヤーのHPが100から90に「変化」しました。

しかし、関数型プログラミングでは、データを「変化」させません。

もしプレイヤー(HP 100)が10のダメージを受けたらどうなるか?

「HPが90になった、新しいプレイヤーのデータ」を作ります。

元の「HP 100のプレイヤー」データは変更しません。

例えるなら、元のファイルを直接上書きするのではなく、常に「別名で保存」して新しいファイルを作るようなイメージです。

特徴2:関数は「道具」であり「材料」

もう一つの特徴は、「関数」そのものを、数値や文字列と同じように「モノ」として扱える点です。(第一級関数と言います)

  • 関数を、別の関数の「材料(引数)」として渡す。
  • 関数が、結果として「新しい関数」を返す。

これにより、「Aリストの全ての数値を2倍する」といった処理を、「数値のリスト」と「2倍する関数」を組み合わせて実現するなど、非常に柔軟な書き方が可能になります。


参照透過性があると、何が嬉しいの?

では、なぜそんなに「参照透過性」にこだわるのでしょうか?

それには、エンジニアにとって嬉しいメリットがたくさんあるからです。

メリット1:プログラムの予測がとても簡単!

これが最大のメリットです。

参照透過性がある関数は、外部の状態に影響されません。

「今、あのオブジェクトのHPはいくつだっけ…?」

「このメソッドを呼ぶ前に、誰かがデータを変更してないかな…?」

といった心配が、一切いらなくなります。

同じ材料を入れれば必ず同じ結果が返ってくるので、プログラムの動作が非常に予測しやすくなります。

メリット2:テストが圧倒的に楽!

プログラムのテストをするときも非常に楽です。

add(10, 5)が15を返すことだけを確認すればOKです。

わざわざ「テスト用のデータベースを準備して…」とか「プレイヤーをHP 100の状態にして…」といった面倒な「事前準備」が不要になります。

メリット3:並列処理に強い

(これは少し高度な話ですが)

参照透過性がある関数同士は、お互いに影響を与えません(副作用がないから)。

そのため、複数の計算を同時に、別々のCPUコアで動かす「並列処理」との相性が抜群に良いのです。


もちろん、デメリットや難しさもある

良いことずくめに見えますが、もちろん「銀の弾丸」ではありません。

デメリット1:学習コスト

オブジェクト指向とは根本的に考え方が異なるため、最初は戸惑うことが多いです。特に「データ(状態)を変更しない」という考え方に慣れるまでが大変かもしれません。

デメリット2:現実世界とのギャップ

現実世界のシステムは、データベースの書き換えや画面の表示など、「状態の変化(副作用)」であふれています。

関数型プログラミングでも、もちろんそうした「副作用」を扱う仕組み(モナドなど)はありますが、その概念が初心者にとっては大きな壁になることが多いです。


今後の学習指針:どう学べばいい?

関数型プログラミングは、知っておくと必ずあなたの武器になります。

オブジェクト指向と関数型プログラミング、どちらが偉いという話ではなく、両方の良いところを知っておくことが大切です。

  1. 「純粋さ」を意識してみるまずは、今あなたが書いているコード(オブジェクト指向でも構いません)の中で、「このメソッドは、外部の状態を変えずに、引数だけで結果を返せないかな?」と考えるクセをつけてみましょう。
  2. map や filter に触れてみる多くのモダンな言語には、リストの各要素に処理を適用する map や、条件に合うものだけを抽出する filter といった関数が用意されています。これらは関数型プログラミングの基本的な道具です。for ループで書く代わりに、これらを使ってみるのが第一歩です。
  3. ハイブリッドな言語で試してみるJavaScript (Reactなど) や Python、Scala、Kotlin といった言語は、オブジェクト指向と関数型の両方のスタイルをサポートしています。こうした言語で、少しずつ関数型のエッセンスを取り入れてみるのも良い学習になりますよ。

考え方に慣れるまでは少し時間がかかりますが、この「参照透過性」という視点を持つだけで、あなたの書くコードがよりシンプルで、バグりにくいものになっていくはずです。頑張ってくださいね!

セイ・コンサルティング・グループの新人エンジニア研修のメニューへのリンク

投稿者プロフィール

山崎講師
山崎講師代表取締役
セイ・コンサルティング・グループ株式会社代表取締役。
岐阜県出身。
2000年創業、2004年会社設立。
IT企業向け人材育成研修歴業界歴20年以上。
すべての無駄を省いた費用対効果の高い「筋肉質」な研修を提供します!
この記事に間違い等ありましたらぜひお知らせください。