14 KiB
🎯 De-sugaring Contract(デシュガリング契約)
作成日: 2025-10-02 対象: すべての新機能実装 関連: 言語進化ロードマップ v2.0
📖 概要
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
なぜ凍結するか:
- VM/LLVM/WASMすべてのバックエンドで恩恵を受ける
- セルフホストの保守性が維持される
- 実装の複雑度が爆発しない
2️⃣ Everything is Box/flow Main.main を維持
原則: すべての値はBox、すべての関数はBox/flowのメソッド
// ✅ 正しい設計
box OptionBox<T> {
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<T> へデシュガリング
local x: IntegerBox? = getSomeValue()
// ↓
local x: OptionBox<IntegerBox> = getSomeValue()
Pulse は下限:
- 最小限の型として利用
- 言語機能には持ち込まない
3️⃣ Effects/Capabilities/Contracts は meta 側
原則: 言語仕様には持ち込まない、metaレイヤーで処理
// ❌ 言語機能化(採用しない)
effect IO {
read(path: StringBox): ResultBox<StringBox>
write(path: StringBox, data: StringBox): ResultBox<Unit>
}
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で伝播
// ✅ 正しいエラー処理(ResultBox + ? 演算子)
box FileBox {
read(path: StringBox): ResultBox<StringBox> {
// エラー時は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 で制御
// ✅ policy制御
debug_assert(internal.isValid()) // dev: panic, prod: nop
// ❌ 挙動差を言語機能に(採用しない)
if DEBUG_MODE {
checkInvariant()
}
policy種別:
- warn: 警告のみ
- audit: ログ記録
- enforce: エラー(dev/prod共通)
🍬 デシュガリング実例集
A. 型システム
Optional型 T?
// 糖衣構文
local x: IntegerBox? = getSomeValue()
// ↓ デシュガリング後
local x: OptionBox<IntegerBox> = getSomeValue()
// OptionBoxは標準ライブラリで実装
box OptionBox<T> {
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
// 糖衣構文
type Result = OkBox | ErrorBox
// ↓ デシュガリング後
type Result = SumBox<OkBox, ErrorBox>
// SumBoxは標準ライブラリで実装
box SumBox<A, B> {
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<R>(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 {} スコープ
// 糖衣構文
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<FutureBox>
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 {} 式
// 糖衣構文
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<ChannelBox>
handlers: ArrayBox<fn(Any)>
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. 糖衣構文
パイプライン演算子 |>
// 糖衣構文
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命令増加: なし(ただの関数呼び出し順序変更)
名前付き引数
// 糖衣構文
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 スコープ
// 糖衣構文
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 マクロ
// 糖衣構文
@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<TestCase>
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 マクロ
// 糖衣構文
@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<T> |
標準ライブラリ | なし | 20 |
A|B |
SumBox<A,B> |
標準ライブラリ | なし | 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. コンパイラフラグ
# デシュガリング前のAST表示
$ hakorune --dump-ast program.hkr
# デシュガリング後のHIR表示
$ hakorune --dump-hir program.hkr
# 最終MIR表示(MIR14のみ)
$ hakorune --dump-mir program.hkr
2. スモークテスト
# デシュガリング検証スイート
$ tools/smokes/v2/run.sh --profile desugaring
3. Linter検出
# レガシー命令検出
$ HAKO_OPT_DIAG=1 hakorune program.hkr
# レガシー命令禁止(Fail-Fast)
$ HAKO_OPT_DIAG_FORBID_LEGACY=1 hakorune program.hkr
🚨 契約違反の例(やってはいけない)
❌ 悪い例1: MIR命令を追加
// ❌ NG: Optional型のために新命令追加
enum MirInstruction {
// ... 既存14命令 ...
SomeCheck(ValueId), // ← 追加してはいけない!
NoneCheck(ValueId), // ← 追加してはいけない!
}
// ✅ OK: OptionBoxで実現
box OptionBox<T> {
is_some(): BoolBox { return me.is_some }
// MirCall で呼び出し(既存命令)
}
❌ 悪い例2: 効果システムを言語機能に
// ❌ NG: 効果システムを型システムに統合
effect IO {
read(path: StringBox): ResultBox<StringBox>
}
fn process() with IO {
// VM/コンパイラが効果を追跡
}
// ✅ OK: meta側で処理(視覚糖衣)
with io {
process() // ログに印を出すだけ
}
❌ 悪い例3: 例外を導入
// ❌ NG: try-catch構文を実装
try {
dangerousOp()
} catch(NetworkError e) {
// ...
}
// ✅ OK: ResultBox + ? 演算子
local result = dangerousOp()? // Err時は早期return
📚 参考資料
- MIR Instruction Set - MIR14命令セット詳細
- 言語進化ロードマップ v2.0 - 実装計画
- Phase 16 Macro Revolution - マクロシステム詳細
- Discoverability問題分析 - 発見性問題と解決策
🎊 まとめ
De-sugaring Contract(デシュガリング契約) により、Hakoruneは:
- ✅ コアは最小 - MIR14命令セットを凍結
- ✅ 糖衣は最強 - すべての新機能をデシュガリング/Box/マクロで実現
- ✅ 保守性最高 - セルフホストの実装容易性維持
- ✅ 一貫性抜群 - Everything is Box哲学貫徹
- ✅ 拡張性無限 - Box/マクロでいくらでも拡張可能
この原則により、次世代言語の標準 を打ち立てます。
作成者: Claude Sonnet 4.5 + ChatGPT Pro 作成日: 2025-10-02 関連: 言語進化ロードマップ v2.0