日本語 · English(未訳)

ADR-53: 束縛右辺の名前解決境界とメモ化の正しさ——ラムダ変数捕獲の遮断・tz 名検査の順序独立

判断: 束縛(本体層 top-level・premise 公開語・糖衣段)の右辺の名前解決は 「束縛・premise 語・前文メンバー・列挙」で閉じる——呼び出し側のラムダ変数には落とさず、 誘導つき静的エラーにする(点に依存する値は引数付き束縛 T(x) = … で明示・使用は T(d))。 あわせて、メモ化キャッシュのヒット時に記録済みの tz 名検査(ADR-40)を現在の predicateAlign で 再実行する——検査の順序独立性と、解決値のクロージャ同一性(labelStack ガードの前提)を両立させる。

(発端=還流第 16 便・収蔵 17: 供給側がメモ化〈5e2b545〉への追随の敵対的レビューで検出した バグ 2 件——①右辺が呼び出し側ローカルを掴む束縛で、最初に評価された点(通例は実体化原点 epoch)の値が全点に焼き付き、エラーも警告も無く点集合が変わる ②ADR-40 の tz 名検査が 文の並び順・単一文内の整列切替で有効/無効が変わる。全 9 再現形を当方 impl で確認・ 2026-08-26 設計者裁定=静的エラー化・1.0 前処置)。

  1. 仕様の隙間の閉じ方: spec §4.8「premise は焼き込まず遅延解決」の遅延解決の対象は premise(前文メンバー・公開語——wkst・weekday 級)であり、呼び出し側の値束縛 (ラムダ変数)は含まない——これを明文化する。従来実装は名前解決が locals 鎖へ素通しで 落ちるため T = ordinalIn(day, month, d) の d が使用箇所の filter(d => …) に偶然 解決されていた(規定外の挙動)。この形を合法(マクロ捕獲)と認めると、定義の意味が使用 箇所の変数名に依存する(filter(x => …) の中では未解決)——「定義がテキストのまま境界を 渡る・定義単体で読める」という言語の根本思想への正面の反であり、衛生(hygiene)問題を仕様に 持ち込む。ゆえに遮断が筋。
  2. 互換影響ゼロの実測: 既存コーパス(当方 607 テスト・doctest 全数・供給側 976 unit・ 配信 11 定義・運用ソース全体)にこの形は無い(第 16 便 §3 の実測——だからこそ両者の既存 テストを素通りしてレビューまで残った)。誘導先(引数付き束縛)は現行で正答することを実測 済み。完全未知名 T = foo + 1 は従来から「未解決の名前」——本判断はそれと同じ面へ 「locals にだけ解決できる名前」を倒す整合的な拡張。
  3. 遮断の実装: 右辺評価の env から locals 鎖を切る(locals: null, parent: null——parent 鎖の用途は locals 解決のみ)。誘導のため遮断前の env を保持し(severedLocalsEnv・ネスト 束縛では最外の遮断点を保持——間接参照 B = T + 0 経由でも「呼び出し側のラムダ変数」を 名指しできる)、「未解決の名前」より先に専用診断を出す。推移形(間接参照・premise 公開語 経由)も遮断が根で断つ——第 16 便 §1 の「是正設計への注意」(直接形だけ塞いでも閉じない・ 依存閉包の設計判断が要る)は、捕獲そのものを非合法化することで依存解析なしに解消する。 引数付き束縛の閉包 env・糖衣段(applyStage)の右辺評価も同一原則(実引数は遮断前の呼び出し側 env で評価——値渡しの意味は不変)。
  4. メモ化は正となる: 束縛右辺が呼び出し側 locals に依存し得なくなるため、premise 名+前文 メンバー 8 成分の文脈キーで値の共有は正しい(第 16 便 §1 の焼き付きは構造的に不可能に なる)。O(N²) 封止(メモ化の本旨・2026-08-03)はそのまま。
  5. tz 名検査の順序独立(§2 の閉じ方): 検査(ADR-40)は値でなく評価器状態 (predicateAlign)に依るため、キャッシュヒットで右辺評価ごと飛ぶと「最初に評価された 文脈の検査 1 回で以降の文脈が素通る」(並び順・単一文内の整列切替のいずれでも・premise 公開語経路にも同型——第 16 便 §2 の全再現形)。閉じ方は検査をキャッシュの外へ出す (第 16 便 §2 の代替案): 右辺評価中に踏んだ predicateAlign 文脈の検査を(win grain・診断名で 重複排除して)キャッシュエントリへ記録し、ヒット時にも現在の predicateAlign で再実行する。 対象は defCache の全 3 経路(本体層束縛・premise 公開語・カレンダー実体の bizDay 標準導出)。
  6. キーに align を足す案・メモ化スキップ案は不採——ともに当方で実測して棄却した。align を キーに足すと、label: 付与式の評価(windowInstance が predicateAlign を設定する)と外側評価とで キーが割れて再評価が走り、解決値のクロージャ同一性が失われて labelStack ガード (ADR-34/42 判断 7 (f))が糖衣循環の別文言に化ける(当方 instance-reference (f) の回帰で 実測——第 16 便が予告した「シャドウ形で保存されない」と同族の、整列切替形での破れ)。 供給側先行是正の「真の自由名によるメモ化スキップ」も判断 1 の裁定(静的エラー化)の下では 不要になり、スキップ判定の罠 2 種(診断の変化・名前衝突での O(N²) 復帰=第 16 便 §1 の 共有知見)ごと回避される。検査の記録・再実行は値の同一性を保ったまま検査だけ毎文脈で 立てる——両立の唯一解に近い。
  7. 性能: 記録は重複排除つき(ユニークな検査は式中の射影箇所数程度)・再実行は tz 名比較 数回で、617 テストの所要は従来と同水準(重複排除なしの素朴な記録は点ごとに積まれ、ヒット時 再実行が O(N²) を戻すことを実測——同じ轍を踏まないための註記)。

経緯: 供給側がメモ化追随の敵対的レビューでバグ 2 件を検出(写経は成功・「写経した意味そのものが 退行を生む」型=還流第 16 便・1.0 宣言の判断材料として番号繰り上げの先行提供)→当方全 9 形 再現→設計者裁定(①静的エラー化 ②1.0 前処置・2026-08-26)→実装(遮断+記録・再実行・ テスト 10 本〈誘導 3 面・正道 3 形・順序 3 形・偽陽性なし 1〉)。供給側の先行是正 2 点 (メモ化スキップ・align 鍵)は本 ADR の公開をもって当方形へ追随・解消される見込み(第 16 便 §3 の申し合わせ)。

追記(2026-08-28・還流第 17 便=供給側の追随レビューが検出した 2 件の処置)

  1. 判断 3 の完遂——引数付き束縛の呼び出し経路(evalCall)の遮断漏れを補綴: 判断 3 は 「引数付き束縛の閉包 env も同一原則」と明記したが、実装は evalDef の lambda 化経路だけ遮断し、 top-level 引数付き束縛の呼び出し(evalCall)が漏れていた——T(x) = dayNo(d) の d が 使用箇所の変数名に依存する(filter(d =>…) では動き filter(e =>…) では未解決・引数は無視) 形が残っていた。焼き付きは無い(lambda は defCache 非搭載)が、spec §4.8 の明文 「使用箇所の変数名に依存しない」への違反。遮断を補い、誘導文言も引数付き束縛の場合に通じる形 (「右辺では x を使い、呼び出し側で T(d) と渡す」)へ改稿。コーパス走査の補(供給側の 問いへの回答): 判断 2 の確認は「新検査の下で既存テスト・doctest・配信定義が全通過」の機械 確認であり、遮断が引数付き束縛へ及んだ本補綴後も 623 テスト・doctest 全数が通過=引数付き 束縛経由の該当形も既存コーパスに無いことが同じ方法で機械確認された。
  2. 判断 5 の記録条件の精密化——fromPredicate 明示+開始時 align 一致: 記録条件が input === predicateAlign の参照等値だったため、値由来の検査(within・ordinalIn の 単位窓×枠窓)が偶然同一オブジェクトになる経路で誤記録され、①ヒット時再実行の診断が実在しない 検査名を名指す(誤帰属)②within だけが立つ束縛で純粋な偽陽性、が生じていた(第 17 便 §2)。 条件を二重に精密化: ①fromPredicate の明示渡し(predicateAlign を入力に据える 5 呼び出し元 だけ・供給側の叩き台案)②predicateAlign === 束縛評価開始時の align(frame.baseAlign)の ときだけ記録——右辺内の filter が自分で確定した align での検査は右辺内で完結し呼び出し文脈に 依存しない(②は当方の追加・供給側が「停止は変えない」と保留した §2-2 の過剰停止も原理的に 解消する)。第 16 便の順序独立 3 形(右辺素の束縛=開始時 align のままの検査)は記録が維持され 不変量テストも無傷。3-1 の対照実験(align 鍵が labelStack ガードを破る)は判断 6 の独立裏付け。

(経緯: 供給側の追随の敵対的レビューが検出=第 17 便・収蔵 19。同便 §4 は第 13 便プレリュードの hour 再定義〈ADR-50 標準と冗長〉の差し替え正本——external.md doctest 化時はそちらを使う。 テスト 5 本追加=623。)

追記 3(2026-08-28・還流第 18 便=追記 2 の記録条件が作った退行の修正)

ヒット時再実行は、評価中の外側束縛のフレームへ再記録する(再実行 3 経路の checkTzMembership に fromPredicate=true——供給側の叩き台 3 行をそのまま採用)。追記 2 で記録条件を fromPredicate の 明示渡しへ精密化した際、再実行 3 経路が既定 false のまま残り、旧条件(参照等値)が暗黙に担って いた「再実行検査の外側フレームへの伝播」が失われていた——結果、B = T の間接参照で内側 T が キャッシュヒットすると検査が B のエントリへ引き継がれず、T を先に単独評価する 1 行を足すだけで 危険な文が黙って通る(判断 5 が明文で否定した「並び順」依存の再来・premise 公開語経路にも同型・ 値は align 非依存で正しいため壊れたのは警告の一貫性)。フィールドコメント「ネスト束縛は全層へ 記録——外側ヒット時は内側評価が走らないため」の意図を、追記 2 自身が破っていた形。修正後は 第 16 便 3 形・第 17 便 §2-2/§2-3 の封止・追記 2 の baseAlign 条件がすべて維持され、間接参照の witness 3 本(並び順 2 経路+単一文内整列切替)を追加=626 テスト・所要は従来水準(判断 7 の dedupe は再記録経路でも有効)。

同時観測(未処置・90-open-questions 採録): bizDay 標準導出経路に別種の順序非対称が残る—— 導出を everyDay 文脈で先に評価すると別 tz 参照で止まり、別 tz 文脈で先に評価すると両順とも通る。 機序は「右辺内 filter が確定した align」と「呼び出し文脈の align」が everyDay align のオブジェクト 共有で偶然一致し、右辺内で完結した検査(追記 2 の裁定では記録対象外)が記録される側の非対称。 完全に切るには「右辺内で predicateAlign が変更されたか」の追跡が要る——最小修正を優先し深追い しない(追記 2 の先行精密化が本退行を生んだ教訓)。1.0 後の精密化候補。

追記 4(2026-08-31・還流第 19 便=テストの網の穴 3 件の処置)

実装は不変(受理境界・診断文言とも)。処置はテスト側のみ——供給側が公開 HEAD 6f909cb の複製に 掛けた変異試験で 3 件を検出、当方 HEAD でも全件再現した。(1) 追記 3 の witness ③(単一文内の整列 切替 × 間接参照)は追記 3 の退行を検出しない——T を先に単独評価する文が無いため(eval.ts を 066d858 へ戻すと ①② は赤・③ は green のまま)。③ は判断 5 の「単一文内切替」側を間接参照込みで 守る別面の witness で無価値ではないが、「本件の回帰 witness 3 本」は過大表記だった→テスト名と注記で 位置づけを明記。(2) 期待値 /入力と窓の tz 名が不一致/ が緩い——診断の入力/窓を左右取り違えても 626 本が通っていた(唯一の赤は playground 焼き込み指紋の同期検査=内容を問わない)。第 17 便 §3-2 の 写経事故と同型。→ 期待値を診断の全文(ctx 名・入力/窓の値・rebase 誘導の末尾)でアンカーつきに固定。 変異 3 種(左右取り違え・ctx 差し替え・末尾追記)で 7 本が赤になることを実測。診断文言は仕様の一部 (誘導が正道を指す設計=文言の固定にこそ価値)であり、実装の自由度としては残さない。(3) defCache 第 3 経路(bizDay 標準導出・eval.ts:1367)に witness が無かった——fromPredicate=true を落としても 626 本が通っていた。→ witness 1 本追加(B = bizDay の間接参照・bizDay を everyDay 文脈で先行評価→ B を everyDay 文脈で評価→K.monthStart 文脈で B 参照=並び順 witness ① の第 3 経路版。HEAD は ERR・true を落とすと 22/22/1 点で黙って通る)。これで追記 3 の回帰 witness は「並び順 2 経路+bizDay 経路」の 3 本、066d858 版で 3 本とも赤。626→627 テスト。

教訓=witness は変異試験で効きを確かめてから数える(供給側も自側で同じ空振りを踏み、変異試験まで 気づかなかったと報告)。同時観測の bizDay 非対称(90-open-questions)は供給側でも同形を再現 (第 19 便 §5・並び順表つき)——1.0 後の精密化候補のまま不変。

追記 5(2026-09-07・供給側の追随完了と独立裏付け——2026-09-08 定期便)

供給側(写経実装)が本 ADR への追随を 3 段階で完了した(初回=判断 1〜6・2 段目=追補 16 の記録条件・3 段目=追記 3・4)と 報告(収蔵 23 §2・unit 1576 passed)。独立裏付けとして価値があるのは二点。①追補 16 が封じた「過剰停止」2 形 (ordinalIn〈単位窓×枠窓〉の誤帰属・within の偽陽性)は供給側の評価器でも正当なプログラムを止めていた—— 当方の新規テスト 2 本を先方が実走して 2 形とも停止=記録条件の精密化は当方実装の癖ではなく判断 5 の記録条件そのものの 問題だった、の確認。②判断 3(引数付き束縛の閉包遮断)は先方が先行実装済みで追随不要=遮断の形が二実装で一致。 語彙・意味論の変更なし。