第 1 課:概要と序論:動的コンポジションと 2 つの次元
一言でいうと:この論文は「実行時にコンポーネントを安全に装着・取り外せる」ことに理論的基盤を与えようとしています。それには、互いに独立した 2 つの次元が同時に成り立つ必要があることを発見しました——時間的には取り外したら元に戻せること(時間的コンポーザビリティ)、空間的には依存関係が自動的に調整されること(空間的コンポーザビリティ)——そして 2 つの鍵を示しています:可逆エフェクトとリアクティブコエフェクトです。Cordis はこの理論の実装であり、DSH はまさにその肩の上に築かれています。
1. 動的コンポジション:実行時に装着・取り外しされるコンポーネント
1.1 まず「静的コンポジション」と「動的コンポジション」を区別する
コンポジション、すなわち単純な部品から複雑なシステムを組み立てることは、ソフトウェア工学の土台となる原則です。
従来、コンポジションは静的です。関数呼び出し、モジュールのインポート、クラス継承はすべてコンパイル時に解決され、プログラムの実行期間中ずっと固定されます:
// 静的コンポジション:コンパイル時に固定され、実行時には変更できない
import { readFile } from "node:fs"; // このステップはコンパイル時に解決済み
function loadConfig() {
return readFile("config.json", "utf8"); // この呼び出し関係は全期間を通じて固定
}
一方、動的コンポジションとは、実行時にコンポーネントをロード、アンロード、再設定することです。コードを変更する必要も、再コンパイルする必要もなく、システムが動いたままの状態で、コンポーネントを装着したり取り外したりできます:
// 動的コンポジション:システム稼働中に、いつでもコンポーネントを着脱できる
installComponent(chatPlugin); // 装着:チャット機能がすぐに使えるようになる
uninstallComponent(chatPlugin); // 取り外し:チャット機能がすぐに消え、システムは動き続ける
| 比較 | 静的コンポジション | 動的コンポジション |
|---|---|---|
| いつ決まるか | コンパイル時 | 実行時 |
| コンポーネントを交換できるか | 交換 = コード変更、再コンパイル | いつでも装着・取り外し・再設定可能 |
| 典型的な例 | 関数呼び出し、import、クラス継承 | プラグインシステム、自己進化エージェントフレームワーク |
🎁 たとえ話:静的コンポジションは「建て終わったビル」です。間取りは図面の時点で固定され、変えたければ耐力壁を壊すしかありません。動的コンポジションは「一組のレゴブロック」で、どのピースもいつでも取り付け、取り外し、場所を変えられます。
1.2 誰が動的コンポジションを必要とするのか?——プラグインシステムと自己進化エージェント
論文は 2 種類の「ユーザー」を挙げています:
- プラグインシステム:VS Code の拡張機能、ブラウザのプラグイン、ゲームの Mod——ユーザーが新しいプラグインをインストールすれば機能がすぐに現れ、プラグインをアンインストールすれば機能がすぐに消えます。プラグインマーケットプレイスの繁栄は、まさに「実行時に装着・取り外しできる」ことに支えられています。
- 自己進化エージェントフレームワーク:エージェントは長期間の稼働の中で、自分に新しいツールを装着し、古いツールを交換し、さらには自分自身のランタイムを変更する必要があります——実行中のシステムが自分自身を変更するのです(DSH の「自己言及的変更」を覚えていますか?)。
1.3 なぜ既存のソフトウェアにはできないのか?
「プラグインシステムならとっくに存在しているのでは?」と言うかもしれません。問題は——装着・取り外しができることと、きれいに装着・取り外しできることは同じではないということです。
現在の実践は粗粒度の仕組みに依存しており、論文は 2 つの問題点をまとめています:
- 再起動でしか再設定できない——重要な設定を変えたい?再起動してください。再起動はすべての実行時状態(メモリ内のセッション、キャッシュ、一時データがすべて消える)を失うことを意味します。
- 取り外してもきれいにならない——プラグインをアンインストールした後も、登録されたイベントリスナーがシステムに「居座って」イベントに応答し続けたり、割り当てられたリソースが回収されなかったりして、システムに大量の「ゾンビ」が残留します。
🎁 たとえ話:これは水槽の水を換えたいのに、まず「魚をすくい出し、水を捨て、また魚を戻す」必要があるようなものです。水を換えたいだけなのに、代償として水槽の生態系全体を作り直すことになります。さらに悪いことに、魚をすくい出した後も、水槽の水草が居座って離れないことがあります。
論文の核心的な主張は、動的コンポジションに欠けているのはエンジニアリングの工夫ではなく、理論的基盤だということです。静的コンポジションには非常に成熟した形式化の枠組み(型システム、圏論……)がありますが、動的コンポジションには同等の理論的裏付けがありません。この論文はその空白を埋めるためのものです。
2. 2 つの直交する次元:きれいに取り外せる × 整然と配置できる
論文は、「動的コンポジションに本当に必要なもの」を特徴づけるには、コンポジションの代数的性質を見るだけでは不十分であり、互いに直交する 2 つの次元を特定しなければならないと主張します。
直交とはどういう意味でしょうか?それは2 つの事柄が互いに独立していて、干渉し合わないということです——片方を解決しても、もう片方がついでに解決されることはありません。そして、どちらか一方でも欠ければ、動的コンポジションは成り立ちません。
2.1 時間的コンポーザビリティ:取り外したら元に戻せる
定義(平易な言葉で):コンポーネントを取り外す際、そのコンポーネントが共有環境に加えたすべての変更を完全かつ安全にロールバックできなければなりません——すべてのリソース割り当て、イベント登録、状態変更が追跡され、コンポーネントの取り外し時に順序立てて回収される必要があります。
言い換えると、コンポーネントは稼働中に「環境に触れます」——メモリを占有し、リスナーを登録し、設定を変更し、ファイルを書き込みます。時間的コンポーザビリティは、それを取り外したときに、触れたものすべてが、まるで最初から存在しなかったかのように元に戻ることを要求します。
🎁 たとえ話:キッチンです。料理中に水を使い、調理台を占領し、火を点け、調味料の位置を動かします。料理が終わったら、すべてを元の位置に戻し、調理台をきれいに拭き、火を消さなければなりません。「料理後にキッチンが元通りになるかどうか」、それが時間的コンポーザビリティです。 有能な料理人は「焦げた鍋をシンクに浸けたまま」にはしません。
静的な環境では、これは簡単です——関数が終了すれば、そのローカル変数は自動的に破棄されます(これが論文で言及されている RAII と波括弧スコープです)。「関数の終了 = キッチンが自動的に元通り」というわけです。しかし動的な環境では、コンポーネントは長期間存続し、状態を持ち、そのエフェクトのスコープは字句の境界に拘束されません——コンポーネントがアンインストールされる時点で、その「キッチン」はシステム全体に散らばっているかもしれないのです。
2.2 空間的コンポーザビリティ:依存関係の自動調整
定義(平易な言葉で):コンポーネントは、互いの依存関係を構造化された検証可能な方法で宣言し、発見し、解決できなければなりません。そして依存関係が変化したときには、コンポーネントのライフサイクルを調整する必要があります。
言い換えると、コンポーネントは孤立していません——コンポーネント A はコンポーネント B が提供するサービスを必要とするかもしれません。空間的コンポーザビリティは、A が「何が必要か」を明確に宣言でき、システムが「誰が提供するか」を自動的に見つけられ、さらに提供者が変化したとき(装着された、アンインストールされた、バージョンが変わった)、依存側が自動的に起動・停止することを要求します。
🎁 たとえ話:コンセントです。電化製品は発電所がどこにあるかを知る必要はありません。標準的なプラグで「220V の交流電源が必要です」と宣言するだけで、コンセントが給電を担います。電化製品を抜いたとき(アンインストール)、それに依存しているもの(たとえばその映像出力を必要とするモニター)が無理に動き続けるべきではありません。「挿せば使え、抜けば止まり、停電すれば自動で遮断される」、それが空間的コンポーザビリティです。
静的な環境では、これは「モジュールのインポート解決」です——import 文がコンパイル時に依存関係を結びつけます。しかし動的な環境では、依存関係は実行中に現れ、消え、さらには正体を変えます。今日は B がこのサービスを提供していても、明日 B がアンインストールされて C に置き換わったら、A はどうすべきでしょうか?これが空間的コンポーザビリティが答える問いです。
2.3 2 つの次元の比較 + 図解
| 時間的コンポーザビリティ | 空間的コンポーザビリティ | |
|---|---|---|
| 扱う問い | 取り外したら元に戻せるか? | コンポーネント同士はどう調整されるか? |
| 管轄するもの | コンポーネントが環境に加える変更(副作用) | コンポーネントが環境に求める要件(依存関係) |
| 生活の例 | 料理後にキッチンが元通り | 挿せば使え、抜けば止まる |
| 静的環境での対応物 | 字句スコープ / RAII | モジュールのインポート解決 |
| 動的環境での難しさ | 長期間存続し状態を持つエフェクトで、スコープが字句の境界に拘束されない | 依存関係が実行中に現れ、消え、正体を変える |
两个维度互相独立(正交):一个管「拆了干不干净」,一个管「组件怎么协调」
💡 図の 2 つのキーワードに注目してください。時間次元は「副作用の完全な取り消し」に対応し、空間次元は「依存関係が揃ってから起動」に対応します。2 つの次元は互いに独立しています——一方は「きれいに取り外せるか」を、もう一方は「整然と配置できるか」を管轄します。
3. 2 つの鍵:エフェクトとコエフェクト
論文は 2 つの古典的な概念を使ってこの 2 つの次元にそれぞれ対処し、それらを「静的解析ツール」から「実行時メカニズム」へと格上げしています。
3.1 エフェクト(effect):計算が環境に対して何をしたか
エフェクト = 計算が生じさせうる副作用——プログラムが世界に与える影響:状態の変更、ファイルの読み取り、ネットワークリクエストの送信、リスナーの登録。
エフェクトは「出力」方向の概念です:何を変更したか?
論文はエフェクトを可逆エフェクト(reversible effects)へと格上げします。すべてのコンテキスト変換に明示的な逆変換が対になります——あなたが行ったすべての操作に「取り消し手順書」が登録されるのです。コンポーネントのアンインストール時には、リストに従って操作を逆順に再生すれば、状態は完全に復元されます:
// 直感版:各操作に「取り消し操作」を登録する
const install = () => {
registerListener("click", onClick); // 実行:リスナーを登録
setConfig("theme", "dark"); // 実行:テーマを変更
};
// コンポーネントのアンインストール = すべての undo を逆順で実行
const uninstall = () => {
setConfig("theme", "light"); // 取り消し 2:テーマを元に戻す
unregisterListener("click", onClick); // 取り消し 1:リスナーを解除
};
🎁 たとえ話:可逆エフェクト = 「全過程の録画 + すべてのフレームを巻き戻せること」。コンポーネントが何をしたか、システムはすべて帳簿に記録しています。アンインストール時には帳簿に従って逆順に精算し、一銭も踏み倒しません。
3.2 コエフェクト(coeffect):計算が環境に何を必要とするか
コエフェクト = 計算がその環境に要求するもの——アクセスする必要のあるリソース、備えている必要のある能力、依存する必要のあるサービス。
コエフェクトは「入力」方向の概念です:何が必要か? これはエフェクトの双対(裏返し)です。エフェクトはプログラムの世界への影響を記述し、コエフェクトはプログラムの世界への要求を記述します。
論文はコエフェクトをリアクティブコエフェクト(reactive coeffects)へと格上げします。コンポーネントは依存関係を型付きコンテキストに登録し、システムはこの依存関係リストを監視します——依存関係が揃えばコンポーネントは自動的にアクティブ化され、依存関係が失われれば自動的に非アクティブ化されます:
// 直感版:依存関係を宣言し、揃ってから起動する
const plugin = {
requires: ["database", "logger"], // コエフェクト:必要なもの
start() { /* database と logger の両方が揃って初めて呼び出される */ },
stop() { /* database が落ちたら自動的に停止 */ },
};
🎁 たとえ話:リアクティブコエフェクト = コンセント + 電力計。電化製品のプラグが「電気が必要です」と宣言し、コンセントは電気があることを検出して初めて給電し、停電すれば自動で遮断します——電化製品自身が発電所を見張る必要はありません。
3.3 まとめの表
| 概念 | 答える問い | 方向 | 生活のたとえ | 論文による格上げ後 |
|---|---|---|---|---|
| エフェクト effect | 何を変更したか? | 世界への影響(出力) | 料理で水を使い、調理台を占領する | 可逆エフェクト(明示的な取り消し付き) |
| コエフェクト coeffect | 何が必要か? | 世界への要求(入力) | 電化製品がコンセントからの給電を必要とする | リアクティブコエフェクト(依存関係が揃えば自動で起動・停止) |
⚠️ 覚え方のコツ:エフェクトは「すること」、コエフェクトは「必要とすること」を見る——一方は時間次元(取り外したら元に戻す)を、もう一方は空間次元(依存関係の調整)を管轄します。
最後に、論文はこの理論を実際に使えるものに仕立てました:Cordis——時空的コンポーザビリティのためのメタフレームワーク(「フレームワークを作るフレームワーク」)で、2 つの部分から構成されます。エフェクト追跡とコエフェクト解決を備えたコアライブラリ、そして設定調整とホットモジュールリプレースメントを備えた宣言的コンポーネントローダーです。DSH はまさに Cordis の上に構築されています——つまりこの論文を精読することは、DSH の土台を学ぶことなのです。
4. 要点の振り返り
- 動的コンポジション = 実行時のコンポーネント着脱:プラグインシステムと自己進化エージェントの両方がこれを必要としています。現在のやり方は「再起動 + 粗粒度の仕組み」に依存しており、状態を失い、取り外しもきれいになりません。
- 2 つの直交する次元:時間的コンポーザビリティ(副作用を元に戻せる取り外し)と空間的コンポーザビリティ(依存関係の自動的な宣言・発見・調整)は互いに独立しており、どちらも欠かせません。
- 静的と動的の落差:静的な世界では、時間的コンポーザビリティ ≈ 字句スコープ(RAII)、空間的コンポーザビリティ ≈ モジュールのインポートですが、動的な世界ではどちらも非常に難しくなります。
- エフェクト = 計算が環境に対して何をしたか(副作用、出力方向)。可逆エフェクトへと格上げ:各操作に明示的な逆変換が対になり、アンインストール時に逆順で再生されます。
- コエフェクト = 計算が環境に何を必要とするか(依存関係、能力、リソース、入力方向)。リアクティブコエフェクトへと格上げ:依存関係が揃えば自動的にアクティブ化され、失われれば自動的に非アクティブ化されます。
🚀 次のセクションでは論文の「動機付けの例」に入ります。プラグインシステムと自己進化エージェントフレームワークという 2 つの実際のシナリオで、動的コンポジションが具体的にどう破綻するのか、そして粗粒度の回避策がなぜ対症療法にすぎないのかを見ていきます。
理解度チェック · 概要と序論
回答を終えたら「解答を送信」をクリックすると、正誤と解説を確認できます。
