【ITニュース解説】The Expression Problem and its solution

2025年09月07日に「Hacker News」が公開したITニュース「The Expression Problem and its solution」について初心者にもわかりやすいように丁寧に解説しています。

作成日: 更新日:

ITニュース概要

「式問題」とは、データ型と処理の両方を、既存コードを修正せずに拡張する際の課題である。オブジェクト指向では型の追加、関数型では処理の追加が容易だが両立は難しい。多重ディスパッチなどがその解決策として注目される。

出典: The Expression Problem and its solution | Hacker News公開日:

ITニュース解説

ソフトウェア開発において、一度作ったプログラムを後から拡張することは日常的に発生する。新しい機能を追加したり、新しい種類のデータに対応したりする必要があるからだ。このとき、既存のコードをなるべく変更せずに、安全かつ簡単に追加・変更できる設計になっていることが理想とされる。しかし、この「拡張のしやすさ」には、実は厄介なジレンマが存在する。その典型的な例として、プログラミング言語の設計分野で古くから知られている「式問題」という課題がある。

式問題を理解するために、まず簡単な計算式を扱うプログラムを考えてみよう。このプログラムは、例えば「5」のような単なる数値や、「3 + 4」のような足し算の式をデータとして表現し、その計算結果を求める機能を持つ。プログラムの拡張として考えられるのは、大きく分けて二つの方向性がある。一つは「新しい操作の追加」だ。例えば、現在は計算結果を求める操作しかないが、式を「(3 + 4)」のような人間が読みやすい文字列として表示する操作を追加したい場合がこれにあたる。もう一つは「新しいデータ型の追加」である。現在は数値と足し算しかないが、これに「8 - 2」のような引き算の式を追加したい場合がそうだ。式問題とは、この「新しい操作の追加」と「新しいデータ型の追加」という二つの異なる方向への拡張を、どちらも簡単に行えるようにすることが非常に難しい、という問題のことを指す。

多くの一般的なプログラミング手法では、この二つの拡張のうち片方を簡単にすると、もう片方が難しくなるというトレードオフが生じる。例えば、オブジェクト指向プログラミングでこの計算式プログラムを設計する場合を考えてみよう。まず、「式」という共通の性質を持つインターフェース(設計図)を定義し、その中に「計算する」というメソッドを宣言する。そして、「数値」クラスや「足し算」クラスが、このインターフェースを実装する形で具体的に作られる。「数値」クラスの「計算する」メソッドは自分自身の値を返し、「足し算」クラスのメソッドは二つの部分をそれぞれ計算して足し合わせた結果を返す。この設計では、「新しいデータ型の追加」は非常に簡単だ。新しく「引き算」クラスを作り、「式」インターフェースを実装して「計算する」メソッドを定義すれば、既存のコードに影響を与えることなく機能を追加できる。しかし、この設計は「新しい操作の追加」には弱い。もし「式を文字列で表示する」という新しい操作を追加したくなった場合、「式」インターフェースに新しいメソッドを追加し、さらにそれを実装している「数値」クラスや「足し算」クラスなど、すべての既存クラスを修正して新しいメソッドを実装し直さなければならない。これは非常に手間がかかり、修正漏れやバグの原因にもなり得る。

一方で、関数型プログラミングでよく用いられるアプローチでは、立場が逆転する。このアプローチでは、データの種類(数値なのか、足し算なのか)を判別できるようなデータ構造を用意し、操作をそのデータ構造を処理する一つの大きな関数として定義する。「計算する」という関数は、渡された式が数値ならその値を返し、足し算なら二つの部分を再帰的に計算して足し合わせる、といった具合に処理を分岐させる。この設計では「新しい操作の追加」が簡単だ。「式を文字列で表示する」という新しい関数を、既存のコードとは独立して作ればよい。しかし、「新しいデータ型の追加」は困難になる。「引き算」という新しいデータの種類を追加した場合、「計算する」関数や「文字列で表示する」関数など、既存のすべての操作関数に「引き算の場合」の処理を追加する修正が必要になってしまう。

このように、どちらのアプローチにも一長一短があり、拡張性のジレンマ、すなわち式問題に直面する。この問題に対するエレガントな解決策の一つとして提案されているのが「Object Algebra」という手法だ。この手法の核心は、「式の構造を定義すること」と「その式をどう解釈するか(操作)」を完全に分離する点にある。まず、式の構成要素を作るためのインターフェース、いわば「式の工場」の設計図を定義する。この設計図には、「数値を作るメソッド」や「足し算を作るメソッド」などが含まれる。次に、具体的な操作は、この「式の工場」の設計図を実装するクラスとして作る。「計算」という操作のためのクラスは、この設計図に従って、数値を作れと言われればその数値を、足し算を作れと言われれば二つの要素を足し算した結果を返すように実装される。同様に、「文字列化」という操作のためのクラスは、数値を文字列に変換し、足し算をプラス記号で連結した文字列を返すように実装される。式そのものは、どの具体的な操作クラスにも依存せず、抽象的な「式の工場」を使って構築される。これにより、新しい操作を追加したければ、新しい操作クラスを作るだけで済む。また、新しいデータ型を追加したければ、「式の工場」の設計図を拡張し、各操作クラスにその拡張部分の実装を追加することで対応できる。このアプローチにより、データと操作を疎結合に保ち、両方向への拡張性を高いレベルで実現することが可能になる。式問題とその解決策を学ぶことは、変更に強く、保守性の高いソフトウェアをいかに設計するかという、すべてのエンジニアにとって重要な課題を考える上で、非常に有益な示唆を与えてくれる。

関連コンテンツ

関連ITニュース