# 🎯 De-sugaring Contract(デシュガリング契約) **作成日**: 2025-10-02 **対象**: すべての新機能実装 **関連**: [言語進化ロードマップ v2.0](./README.md) --- ## 📖 概要 **De-sugaring Contract(デシュガリング契約)** は、Hakoruneの**最も重要な設計原則**です。 > **新構文は既存構文・既存Boxへ有限段で必ず落ちること。IR命令の追加は最後の手段。** この原則により、**「コアは最小・糖衣は最強」** を実現します。 --- ## 🎯 5つの不変ルール(赤線) ### **1️⃣ MIR14は増やさない** **原則**: すべてデシュガリング/静的パス/標準ライブラリで実現 **現状のMIR14命令セット**: - 基本演算(5): Const, UnaryOp, BinOp, Compare, TypeOp - メモリ(2): Load, Store - 制御(4): Branch, Jump, Return, Phi - 呼び出し(1): MirCall(Callee で Global/Extern/ModuleFunction/Method/Constructor/Closure/Value を表現) - GC(2): Barrier, Safepoint - 構造(2): Copy, Nop(最適化/検証用・意味論不変) 📖 **詳細**: [MIR Instruction Set](../../../reference/mir/INSTRUCTION_SET.md) **なぜ凍結するか**: - VM/LLVM/WASMすべてのバックエンドで恩恵を受ける - セルフホストの保守性が維持される - 実装の複雑度が爆発しない --- ### **2️⃣ Everything is Box/flow Main.main を維持** **原則**: すべての値はBox、すべての関数はBox/flowのメソッド ```nyash // ✅ 正しい設計 box OptionBox { value: T is_some: BoolBox birth(v: T) { me.value = v me.is_some = true } unwrap(): T { if not me.is_some { panic("unwrap on None") } return me.value } } // T? → OptionBox へデシュガリング local x: IntegerBox? = getSomeValue() // ↓ local x: OptionBox = getSomeValue() ``` **Pulse は下限**: - 最小限の型として利用 - 言語機能には持ち込まない --- ### **3️⃣ Effects/Capabilities/Contracts は meta 側** **原則**: 言語仕様には持ち込まない、metaレイヤーで処理 ```nyash // ❌ 言語機能化(採用しない) effect IO { read(path: StringBox): ResultBox write(path: StringBox, data: StringBox): ResultBox } fn process() with IO { // IO効果を要求 } // ✅ meta側で処理(視覚糖衣のみ) with net.out, fs.read { http.get("https://api.example.com") file.read("config.txt") } // ↓ 実際は何も変えない(ログに印を出すだけ) ``` **効果システムの取り扱い**: - **静的解析ツール**で検出(Linter/型チェッカー) - **ランタイムは関与しない** - **視覚的な開発体験向上**のみ --- ### **4️⃣ 例外は導入しない** **原則**: panicはVM/実装バグ用、通常はResultBoxで伝播 ```nyash // ✅ 正しいエラー処理(ResultBox + ? 演算子) box FileBox { read(path: StringBox): ResultBox { // エラー時はResultBox::Err返却 } } flow Main.main() { local content = FileBox::read("config.txt")? // ? で伝播 processContent(content) } // ❌ 例外(採用しない) try { let content = FileBox::read("config.txt") } catch(FileNotFoundError e) { // ... } ``` **panic の用途**: - VM内部エラー(配列境界外アクセス等) - 実装バグ(unreachable!相当) - **通常のエラーハンドリングには使わない** --- ### **5️⃣ dev→prod の挙動差は policy のみ** **原則**: warn/audit/enforce で制御 ```nyash // ✅ policy制御 debug_assert(internal.isValid()) // dev: panic, prod: nop // ❌ 挙動差を言語機能に(採用しない) if DEBUG_MODE { checkInvariant() } ``` **policy種別**: - **warn**: 警告のみ - **audit**: ログ記録 - **enforce**: エラー(dev/prod共通) --- ## 🍬 デシュガリング実例集 ### **A. 型システム** #### **Optional型 `T?`** ```nyash // 糖衣構文 local x: IntegerBox? = getSomeValue() // ↓ デシュガリング後 local x: OptionBox = getSomeValue() // OptionBoxは標準ライブラリで実装 box OptionBox { value: T is_some: BoolBox birth(v: T) { me.value = v; me.is_some = true } birth_none() { me.is_some = false } unwrap(): T { /* ... */ } is_some(): BoolBox { return me.is_some } is_none(): BoolBox { return not me.is_some } } ``` **MIR命令増加**: なし(既存のMirCall, Branch, Return等で実現) #### **Union型 `A|B`** ```nyash // 糖衣構文 type Result = OkBox | ErrorBox // ↓ デシュガリング後 type Result = SumBox // SumBoxは標準ライブラリで実装 box SumBox { tag: StringBox // "A" or "B" value_a: A value_b: B birth_left(a: A) { me.tag = "A"; me.value_a = a } birth_right(b: B) { me.tag = "B"; me.value_b = b } match_sum(on_left: fn(A) -> R, on_right: fn(B) -> R): R { if me.tag == "A" { return on_left(me.value_a) } else { return on_right(me.value_b) } } } ``` **MIR命令増加**: なし --- ### **B. 並行処理** #### **`async {}` スコープ** ```nyash // 糖衣構文 async { nowait task1 = longTask1() nowait task2 = longTask2() // スコープ終了で自動待機 } // ↓ デシュガリング後 local group = new TaskGroupBox() group.spawn(fn() { longTask1() }) group.spawn(fn() { longTask2() }) group.await_all() // スコープ終了時自動 // TaskGroupBoxは標準ライブラリで実装 box TaskGroupBox { tasks: ArrayBox spawn(task: fn()) { local future = nowait task() me.tasks.push(future) } await_all() { loop(me.tasks.len() > 0) { local future = me.tasks.pop() await future } } } ``` **MIR命令増加**: なし(既存のnowait/awaitで実現) #### **`select {}` 式** ```nyash // 糖衣構文 match select { ch1.receive() => { handle1() } ch2.receive() => { handle2() } timeout(1000) => { handleTimeout() } } // ↓ デシュガリング後 local sel = new SelectBox() sel.on(ch1, fn(v) { handle1() }) sel.on(ch2, fn(v) { handle2() }) sel.on_timeout(1000, fn() { handleTimeout() }) sel.run() // SelectBoxは標準ライブラリで実装 box SelectBox { channels: ArrayBox handlers: ArrayBox on(ch: ChannelBox, handler: fn(Any)) { me.channels.push(ch) me.handlers.push(handler) } on_timeout(ms: IntegerBox, handler: fn()) { /* ... */ } run() { // poll all channels, invoke first ready handler } } ``` **MIR命令増加**: なし --- ### **C. 糖衣構文** #### **パイプライン演算子 `|>`** ```nyash // 糖衣構文 value |> f(_) |> g(_, 42) |> h(_) // ↓ デシュガリング後 h(g(f(value), 42)) // 実用例 [1, 2, 3, 4, 5] |> map(_, fn(x) { x * 2 }) |> filter(_, fn(x) { x % 3 == 0 }) |> reduce(_, 0, fn(acc, x) { acc + x }) // ↓ デシュガリング後 reduce(filter(map([1,2,3,4,5], fn(x){x*2}), fn(x){x%3==0}), 0, fn(acc,x){acc+x}) ``` **MIR命令増加**: なし(ただの関数呼び出し順序変更) #### **名前付き引数** ```nyash // 糖衣構文 download(url: u, timeout: 3.s, retry: 5) // ↓ デシュガリング後(位置引数 + デフォルト補完) download(u, 3.s, 5) // 欠けた引数はデフォルト補完 download(url: u) // ↓ download(u, DEFAULT_TIMEOUT, DEFAULT_RETRY) ``` **MIR命令増加**: なし(静的パスで位置引数に変換) #### **`with capability` スコープ** ```nyash // 糖衣構文 with net.out, fs.read { http.get("https://api.example.com") file.read("config.txt") } // ↓ デシュガリング後(何も変えない) // metaレイヤーでログに印を出すだけ http.get("https://api.example.com") file.read("config.txt") ``` **MIR命令増加**: なし(完全に視覚糖衣) --- ### **D. マクロ** #### **`@test` マクロ** ```nyash // 糖衣構文 @test it("should add numbers") { local calc = new Calculator() expect(calc.add(2, 3)).toBe(5) } // ↓ デシュガリング後(HIRパッチ) TestRunnerBox::register("should add numbers", fn() { local calc = new Calculator() expect(calc.add(2, 3)).toBe(5) }) // TestRunnerBoxは標準ライブラリ static box TestRunnerBox { tests: ArrayBox register(name: StringBox, test: fn()) { me.tests.push(new TestCase(name, test)) } run_all() { loop(me.tests.len() > 0) { local test = me.tests.pop() test.run() } } } ``` **MIR命令増加**: なし(HIRレベルでパッチ) #### **`@profile` マクロ** ```nyash // 糖衣構文 @profile calculate() { // 処理 } // ↓ デシュガリング後(HIRパッチ) calculate() { local _prof = ProfileBox::start("calculate") // 処理 _prof.end() } // ProfileBoxは標準ライブラリ box ProfileBox { name: StringBox start_time: TimeBox static start(name: StringBox): ProfileBox { local prof = new ProfileBox() prof.name = name prof.start_time = TimeBox::now() return prof } end() { local duration = TimeBox::now() - me.start_time ConsoleBox::log("Profile: " + me.name + " took " + duration.toString()) } } ``` **MIR命令増加**: なし --- ## 📊 デシュガリング規則一覧表 | 構文 | デシュガリング後 | 実装方法 | MIR命令増加 | Phase | |------|----------------|---------|------------|-------| | `T?` | `OptionBox` | 標準ライブラリ | なし | 20 | | `A\|B` | `SumBox` | 標準ライブラリ | なし | 20 | | `x \|> f(_)` | `f(x)` | 静的パス(AST変換) | なし | 17-18 | | `f(a:1, b:2)` | `f(1,2)` | 静的パス(引数並び替え) | なし | 18-19 | | `async { body }` | `TaskGroupBox::scoped(...)` | 標準ライブラリ | なし | 22 | | `select { ... }` | `SelectBox::new()...run()` | 標準ライブラリ | なし | 23 | | `@test it(...){...}` | `TestRunnerBox::register(...)` | マクロ(HIRパッチ) | なし | 16.1 | | `@bench(iter:N) { ... }` | `BenchmarkBox::run(...)` | マクロ(HIRパッチ) | なし | 16.3 | | `@profile { ... }` | `ProfileBox::start()` + `.end()` | マクロ(HIRパッチ) | なし | 20 | | `repr(obj)` | `@derive(Debug)` 連携 | マクロ(HIRパッチ) | なし | 18 | | `x.move()` | 所有権移転メソッド | 標準ライブラリ | なし | 28 | | `x.share()` | Arc参照増加メソッド | 標準ライブラリ | なし | 28 | | `x.weak()` | 弱参照取得メソッド | 標準ライブラリ | なし | 28 | | `with cap { ... }` | (何も変えない) | 視覚糖衣(meta) | なし | 20-22 | | `comptime { ... }` | 定数埋め込み | 静的パス(ビルド時評価) | なし | 22-25 | | `pattern Some(x)` | `OptionBox::Some(x)` | 静的パス(パターン別名) | なし | 23-25 | **重要**: すべてMIR命令増加なし! --- ## 🔍 デシュガリング検証方法 ### **1. コンパイラフラグ** ```bash # デシュガリング前のAST表示 $ hakorune --dump-ast program.hkr # デシュガリング後のHIR表示 $ hakorune --dump-hir program.hkr # 最終MIR表示(MIR14のみ) $ hakorune --dump-mir program.hkr ``` ### **2. スモークテスト** ```bash # デシュガリング検証スイート $ tools/smokes/v2/run.sh --profile desugaring ``` ### **3. Linter検出** ```bash # レガシー命令検出 $ HAKO_OPT_DIAG=1 hakorune program.hkr # レガシー命令禁止(Fail-Fast) $ HAKO_OPT_DIAG_FORBID_LEGACY=1 hakorune program.hkr ``` --- ## 🚨 契約違反の例(やってはいけない) ### **❌ 悪い例1: MIR命令を追加** ```rust // ❌ NG: Optional型のために新命令追加 enum MirInstruction { // ... 既存14命令 ... SomeCheck(ValueId), // ← 追加してはいけない! NoneCheck(ValueId), // ← 追加してはいけない! } // ✅ OK: OptionBoxで実現 box OptionBox { is_some(): BoolBox { return me.is_some } // MirCall で呼び出し(既存命令) } ``` ### **❌ 悪い例2: 効果システムを言語機能に** ```nyash // ❌ NG: 効果システムを型システムに統合 effect IO { read(path: StringBox): ResultBox } fn process() with IO { // VM/コンパイラが効果を追跡 } // ✅ OK: meta側で処理(視覚糖衣) with io { process() // ログに印を出すだけ } ``` ### **❌ 悪い例3: 例外を導入** ```nyash // ❌ NG: try-catch構文を実装 try { dangerousOp() } catch(NetworkError e) { // ... } // ✅ OK: ResultBox + ? 演算子 local result = dangerousOp()? // Err時は早期return ``` --- ## 📚 参考資料 - [MIR Instruction Set](../../../reference/mir/INSTRUCTION_SET.md) - MIR14命令セット詳細 - [言語進化ロードマップ v2.0](./README.md) - 実装計画 - [Phase 16 Macro Revolution](../phases/phase-16-macro-revolution/README.md) - マクロシステム詳細 - [Discoverability問題分析](./discoverability-analysis.md) - 発見性問題と解決策 --- ## 🎊 まとめ **De-sugaring Contract(デシュガリング契約)** により、Hakoruneは: 1. ✅ **コアは最小** - MIR14命令セットを凍結 2. ✅ **糖衣は最強** - すべての新機能をデシュガリング/Box/マクロで実現 3. ✅ **保守性最高** - セルフホストの実装容易性維持 4. ✅ **一貫性抜群** - Everything is Box哲学貫徹 5. ✅ **拡張性無限** - Box/マクロでいくらでも拡張可能 この原則により、**次世代言語の標準** を打ち立てます。 --- **作成者**: Claude Sonnet 4.5 + ChatGPT Pro **作成日**: 2025-10-02 **関連**: [言語進化ロードマップ v2.0](./README.md)