Reactive Coeffects:空間維度的依賴回應
論文用 Reactive Coeffects 解決 spatial composability——元件依賴出現、消失或更換後,系統能否自動調整生命週期與依賴連接。核心思想:元件宣告自己需要什麼(coeffect specification),執行時在上下文變化時按規格通知元件。
Coeffect Context 與 set 即 effect
【論文原文結論 §3.2.1】Coeffect context Σ ≔ (k : K) ⇀ 𝒱_k 是依賴鍵到型別化值的有限偏函式。get 與 set 是兩個核心操作(Def 23),且 set(k, v) 本身就是一個 effect——coeffect 操作就是 effect,coeffect provision 自動繼承可追蹤性與可恢復性。這是 Revertible Effects 與 Reactive Coeffects 的協同點:宣告一個依賴(provide)的同時就在做一個可撤銷的 effect。
Coeffect Specification 與三態變化
【論文原文結論 §3.2.2】Coeffect specification d ⊆ K 是元件宣告的依賴鍵集合;satisfaction predicate σ ⊧ d ≔ ∀k ∈ d. k ∈ dom(σ)。任何把 σ 變為 σ' 的 effect 可按 d 分類為 activating / deactivating / neutral(Def 26):
- activating:變化前 σ 不滿足 d,變化後 σ' 滿足——元件應被啟用;
- deactivating:變化前滿足,變化後不滿足——元件應被卸載;
- neutral:變化前後都滿足或都不滿足——元件無需切換狀態(但依賴實例可能已換)。
資料庫 A + RAG B 完整案例
具體案例(貫穿全文的標準例子):
插件 A(資料庫插件):set('database', dbService) 提供依賴
插件 B(RAG 插件):宣告 d_B = {'database'} 需要依賴- A 出現:A 透過
ctx.set('database', dbService)提供資料庫服務;σ 中'database'鍵出現。B 宣告的d_B = {'database'}由不滿足變為滿足——activating,B 被通知並自動啟用,從ctx.get('database')拿到 dbService。 - A 消失或被替換:A 的 fiber 進入 Unloading,
'database'鍵從 σ 移除。B 的d_B由滿足變為不滿足——deactivating,B 被通知並自動卸載(其自身的 inverse accumulator 被應用,清理 B 佔用的資源)。 - 新 A 就緒:新版資料庫插件 A' 透過
ctx.set('database', newDbService)提供。B 的d_B再次由不滿足變為滿足——activating,B 使用新依賴重新啟用,拿到 newDbService。
整個過程中,B 的程式碼不需要寫「如果 A 不在了就……」的主動檢測——執行時按 coeffect 規格自動通知。
Committed View vs Target View
【論文原文結論 §4.1, §4.2】Cordis 實作中,有兩份關鍵的依賴解析視圖:
- Committed View(ω):當前已提交的依賴解析,記錄 fiber 名到 provider 的對應。元件在整個 episode 中讀到的是這份視圖,保證一個元件的本次生命週期內依賴不變(參見 Ordering 定理)。
- Target View:期望 fiber 應處於的依賴解析狀態,由
refresh在設定變化時重算。當 target 與 committed 不一致時,觸發生命週期轉換(啟用、卸載或重載)。
【論文原文結論 §4.2】關鍵細節:target view 記錄的是 provider identity(fiber uid),而不是 value 是否相等。論文明確指出:「makes the comparison usable, since a different fiber providing an equal value would otherwise compare equal」——如果只比 value,提供相同 value 的不同 fiber 會被誤判為「依賴沒變」,從而不觸發重新啟用,導致 consumer 用著舊 fiber 的快照而不知 provider 已換。fiber.target 在 Cordis 實作中存的是依賴解析結果的 uid 摘要(§5.1.3)。
Isolation 與 Interception
【論文原文結論 §3.2.3】兩個正交的派生機制:
- Isolation:透過 isolation realm table
ρ : K ⇀ R,同一 key k 在不同 context 解析到不同 realm r,從而綁定到不同值。ctx.isolate(k, r)派生一個新 context,僅覆蓋 ρ 在 k 處的對應(derived realization,不修改父表)。 - Interception:在 coeffect 上掛元資料
ι : K → ℙ_k,provider 函式接收 metadata 並據此調整行為。ctx.intercept(k, ν)派生 context 合併 ν 到 k 處的元資料。
兩者區別:Isolation 替換「key 解析到什麼 binding」(換實例),Interception 修改「binding 被使用的方式」(加約束,不換實例)。Isolation 是 derived realization(不需要 inverse,丟棄子 context 即可),而 set 是 in-place realization(需要 inverse 來撤銷)。
場景對照
| 場景 | 用 Isolation | 用 Interception |
|---|---|---|
| 多租戶 / 不同 Workspace | 同一 key 'db' 各租戶解析為各自實例 | — |
| 子 Agent 獨立環境 | 同一 'filesystem' 各子 Agent 獨立實例 | — |
| 測試替身 | 用 mock realm 覆蓋 | — |
| 檔案系統唯讀權限 | — | 掛 readonly metadata |
| 不同插件用不同模型 | 'model' key 各自隔離 | — |
| 社群插件嚴格存取策略 | — | 掛 capability 限制 metadata |
Reactive Coeffects 解決了「依賴變化的感知」,但元件實例自己的載入/卸載/重載過程仍需一個狀態機來管。見 生命週期與卸載順序。