Revision
Rev. | Date | Note |
---|---|---|
0.1.0 |
2024-07 | 社内技術トーク向けに作成 |
0.1.1 |
2025-01 | カリー化の脚注を修正 |
説明内容には諸説あります。
動機
宣言的アプローチによるアーキテクチャ(構造物)が一般的になってきたため
宣言的アプローチとは
オブジェクト指向が一般化した後に取り入れられてきたプログラミングパラダイム(規範)の一つ。宣言的 UI、関数型言語など、近年(と言ってもすでに十年以上になるが)のモダンと呼ばれる手法の多くがこのパラダイムのもとに実装されている。
詳細は後述する。
例)React.js, Rust, Swift, SwiftUI, Java 8 の関数型機能の導入など
→ Appendix: なぜ宣言的アプローチという用語を使うのか?
なぜ宣言的アプローチが必要なのか
オブジェクト指向プログラミングの弱点を補完するため
大規模プログラムは、オブジェクト指向によりプログラム単位を小さくすることで再利用性・保守性を高めることができたが、そのカプセル化によりオブジェクト内部へのデータ隠蔽をともなうことになった。各オブジェクトは内部データの変化によりその振る舞いが変わるため、オブジェクト間の関係性が増えるごとにデータの掛け合わせ状態が増えることになり、プログラム実行時の予測性の難しさをもたらすことになった。
この課題を解決する手段の一つが、宣言的アプローチによるプログラミングであると考える。
宣言的アプローチとは何か (既存の説明編)
一般的には「宣言型プログラミング」という用語が使用されるため、これをもとに確認してみる。
Wikipedia 「宣言型プログラミング」
宣言型プログラミング(英: Declarative programming)は、数理論理学的な性質を表わしている総称的なプログラミングパラダイムである。式の計算構造を、主に表示的意味論下のロジックで表現する構文にされることが多く、式枠外の副作用を伴なう制御フローや自由変数の多用などは排除されるようになる。計算構造は演繹的に組み立てられることが多い。命令型プログラミングと対をなしてのプログラミング言語の分類用語としても扱われている。
→「数理論理学的な性質」「総称的なプログラミング」がキーワードであることは分かるが、更なる用語理解を必要とする難解な説明となっている。
ChatGPT 3.5 「宣言型プログラミングの概要を説明して」
宣言型プログラミングは、プログラムが何を行うかを明確に定義し、具体的な手順や手法を指示するのではなく、プログラムが達成すべき目標や条件を宣言するプログラミングパラダイムです。つまり、プログラマーは目標を設定し、それを達成するための手段や手順を詳細に指示するのではなく、目標自体をプログラムに伝えます。
→「具体的な手順や手法を指示するのではなく」「目標自体をプログラムに伝える」など、ロジックを組まないかのような印象を受ける説明となっている。どのようにすれば宣言型プログラミングが可能なのか分からない。
日経 XTECH 良いコードを書くための技術
処理の記述を並べる代わりに欲しい結果を書く「宣言的プログラミング」──2023.02.08
大雑把に言ってしまえば、ある処理を行いたいときに、「どういう計算を行っていけば欲しい結果が手に入るのか」を書くのが手続き的で、「どういう結果が欲しいのか」を素直に書くのが宣言的になります。
例として以下のような JavaScript の関数を挙げて比較している。
- 手続き的な関数
function sumOfEvenSquare(array) { let sum = 0; for (const v of array) { if (v % 2 === 0) { sum += v * v; } } return sum; } sumOfEvenSquare([1, 2, 3, 4]); // 20
- 宣言的な関数
function sumOfEvenSquare(array) { return array .filter(v => v % 2 === 0) .map(v => v * v) .reduce((r, v) => r + v); } sumOfEvenSquare([1, 2, 3, 4]); // 20
→ 単に組み込み関数を使用すれば宣言的な関数になるかのような説明となっている。sumOfEvenSquare
関数を実装側ではなく利用側でみれば、「どういう結果が欲しいのか」の観点ではどちらも変わらないように見える。
所感
これ以外の説明を確認しても、命令型・手続き型と対をなす概念という定義に引きずられてロジックを組まないことや、既存の HTML や SQL などの宣言的な DSL(ドメイン特化言語)を使用することが宣言的プログラミングであるかのような説明となっているものが多い。
宣言的アプローチとは何か (理解編)
既存の説明を確認しても腑に落ちる理解ができなかったため参照透過性、副作用の排除などのキーワードから帰納的に考えてみると、
「宣言的」とは、使用対象自体から使用結果の充分な予測可能性が得られること。
が、その本質にあるように思う。
簡単に言えば、それを使う前にそれを使った結果を予測できる、ということである。
この本質は裏を返せば、使用結果の充分な予測可能性が得られないことを使用対象自体に含めることができないということでもある。ここから宣言的アプローチによるプログラミングの以下の特性を自ずと導き出すことができる。
→ 予測できない副作用は排除または分離する必要がある
→ 副作用の要因となる状態を保持することができなくなる
→ 参照透過性1を得られる
→ 遅延評価2によるパフォーマンス性の向上が可能となる
→ カリー化3、部分適用、関数合成による再利用性の向上が可能となる
→ 並行処理の安全性の向上が可能となる
このような考え方に基づけば、宣言的アプローチに基づく実装とは、実装方法などの HOW ではないことが分かる。上で挙げた sumOfEvenSquare
関数も、この観点で見ればどちらも宣言的アプローチに基づく実装であると言える。
宣言的アプローチによるプログラミングは、オブジェクト指向プログラミングによる予測性の低下を改善できる技術であり、両者は対立する概念ではない。Java に関数型機能を取り込めたように、既存のプログラムにおいても宣言的アプローチによって改善できることがまだまだあるはずである。
何を宣言しているのか?4
考えるに、対象自体が自らの使用結果を使用者に前もって明らかにしていることを宣言しているとみなして、「宣言的」と表現している。
ハサミを例にすれば、向かい合った片刃が一点で止められた構造はモノを挟んで切ること(切断という結果)が明らかであるし、その動作原理から使用に当たって電力などを消費することがない(他に副作用がない)ということも使用者には明らかである。この点で、ハサミは宣言的な道具であると言える。
宣言的アプローチを開発に取り入れよう
予測のしやすさを常に念頭において作る
使用結果の予測がしやすい宣言的アプローチによるプログラミングはライブラリ作成に適していると言えるが、単に使う側に立ったとしても、自分の作成したコードは必ずメンテナンスする人によって使われる側に回ることになる。その時に重要なことが、予測がしやすいかどうかということである。
UI
- どこに何があるか、その操作の結果どうなるかが見た目から明快であること
- 説明を必要とするような UI はその時点で宣言的とは言えない
- Apple 製品に説明書がないのは宣言的な UI だという表明でもある
(デザイン重視で予測性を犠牲にしている部分もある5)
- Apple 製品に説明書がないのは宣言的な UI だという表明でもある
- 説明を必要とするような UI はその時点で宣言的とは言えない
プログラム
- 使用者に対する事前情報が重要になるため、「名は体を表す」を厳密に考えること
- 事前情報には識別子はもちろん、型、注釈、制約などプログラム言語に依存した付加情報を含む
- 事前情報から予測できない参照透過性を壊す処理は含めてはいけない
- 逆に事前情報から参照透過性でないことが予測できれば含めてもよい
ドキュメント
- 文章を構造化し、ガイドを付加すること
- 文章の全体像は最後まで読み取らないと把握できないため、構造化し展開を予測しやすくする
- 長い文章には要約文を付加して内容を予測しやすくする
Appendix
なぜ宣言的アプローチという用語を使うのか?6
「宣言型 or 宣言的プログラミング」という用語では HOW に比重が偏るため、あえて「宣言的アプローチ」という視点・考え方を意識させる用語とした。オブジェクト指向プログラミングの普及時にも継承やポリモーフィズムといった HOW の習得が先行していたように思うが、本稿はまずは宣言的プログラミングの本質が何かを考え、それを開発現場に活かすことにフォーカスしている。
参考サイト
-
参照透過性とは、どこで使用しても同じ入力に対して常に同じ出力を返す関数や式の性質のこと。英語の "referential transparency" の直訳のため、「透過性」は「不透明性」の逆で明瞭であることだが、参照が何を指しているのかは実はよく分かっていない。結果明瞭性と言った方が用語としては有意義であろう。 ↩
-
遅延評価とは、条件が満たされるまで計算を遅らせたり、一度計算した結果を再利用することで計算量を最適化しパフォーマンスを向上させることができる処理方式のこと。参照透過性により同一入力に対する結果がキャッシュ可能になり、遅延評価が容易となる。処理方式は処理系に依存するため、宣言的アプローチによっても必ずしもパフォーマンス向上につながるわけではない。 ↩
-
カリー化とは、複数の引数を取る関数を、一つの引数を取る関数の連続した呼び出しに変換すること。カリー化により引数を一つにすることで部分適用や関数合成も容易となる。カリー化、部分適用、関数合成は高階関数の使用法であり参照透過性を前提とするものではないが、再利用性の向上という目的を達成する上では必要な要件といえる。 ↩
-
「宣言的」という用語には明確な定義がないことが分かる。Robert Harper──What, If Anything, Is A Declarative Language?. 2013. ↩
-
iPad の画面分割、マルチタスク操作など。 ↩
-
言葉は脳に対するプログラム言語である。新しい技術の習得時には、まずはその技術の本質に気をつけるためにも用語に対する意識は疎かにしない方が良い。 ↩