Files
hakorune/docs/private/roadmap/phases/phase-20-variant-box/DESIGN.md

10 KiB
Raw Blame History

VariantBox設計詳細

作成日: 2025-10-08 設計元: ChatGPT Pro提案 + ultrathink 4タスク分析 状態: 承認済み(セルフホスト完了後に実装)


🎯 設計目標

哲学的整合性

  • Everything-is-Box: VariantBox自体がBox、tag/fieldsもすべてBox
  • MIR16維持: 新命令なし、すべて既存のnewbox/boxcallに脱糖
  • 決定性: ArrayBoxベースで再現性保証MapBoxより優位
  • Plugin-First: 開いたタグ拡張可能EnumSchemaBox.extend()

技術的目標

  • 型安全性向上(閉じた型システム)
  • パターンマッチング構文
  • Option/Resultの改善

🏗️ Core設計

VariantBox構造

// 判別共用体の最小表現
box VariantBox {
    tag: StringBox           // Variant識別子Phase 25でSymbolBox化可能
    fields: ArrayBox         // 位置引数常に配列、統一的API

    birth(tag_str) {
        me.tag = new StringBox(tag_str)
        me.fields = new ArrayBox()
    }

    // 判定
    is_tag(name: StringBox) -> BoolBox {
        return me.tag.equals(name)
    }

    // フィールドアクセスFail-Fast
    field(i: I64Box) -> Box {
        if i < 0 || i >= me.fields.len() {
            panic("VariantBox.field: index " + i.to_s() +
                  " out of bounds (len=" + me.fields.len().to_s() + ")")
        }
        return me.fields.get(i)
    }

    // 安全版Option返却、Phase 20.9で実装)
    try_field(i: I64Box) -> OptionBox {
        if i < 0 || i >= me.fields.len() {
            return Option.none()
        }
        return Option.some(me.fields.get(i))
    }

    // シリアライズ(決定性キャッシュ用)
    to_data() -> DataBox {
        return DataBox.of_array([
            DataBox.of_string(me.tag.to_s()),
            DataBox.of_array(me.fields.map(|f| (f as BaseValue).to_data()?))
        ])
    }

    // デバッグ
    debug() -> StringBox {
        return me.tag + "(" + me.fields.join(", ") + ")"
    }
}

設計判断:

選択肢 採用 理由
タグ型 StringBox SymbolBox未実装、即座に開始可能
フィールド型 ArrayBox MapBoxより決定性高い、順序保証
Arity 可変0-N 単一値も複数値も統一的に扱える

EnumSchemaBox型安全性検証

// enum定義のメタ情報管理
static box EnumSchemaBox {
    name: StringBox              // "Result", "Option" 等
    variants: MapBox             // "Ok" -> VariantSchema

    register_variant(tag: StringBox, field_names: ArrayBox) {
        local schema = new VariantSchema()
        schema.tag = tag
        schema.field_names = field_names
        schema.arity = field_names.len()
        me.variants.set(tag, schema)
    }

    validate(v: VariantBox) -> Result {
        local schema = me.variants.get(v.tag)
        if schema == null {
            return Result.err("Unknown variant: " + v.tag)
        }
        if v.fields.len() != schema.arity {
            return Result.err("Arity mismatch: expected " + schema.arity +
                              ", got " + v.fields.len())
        }
        return Result.ok()
    }

    // プラグイン拡張(開いたタグ対応)
    extend(tag: StringBox, arity: I64Box) {
        me.register_variant(tag, [])  // フィールド名なし版
    }
}

// Variant定義の詳細
box VariantSchema {
    tag: StringBox
    field_names: ArrayBox        // ["value"], ["lhs", "rhs"] 等
    arity: I64Box

    // 名前付きアクセスPhase 20.9で実装)
    field_index(name: StringBox) -> I64Box {
        return me.field_names.index_of(name)
    }
}

📋 @enumマクロ脱糖

入力構文

@enum Result {
    Ok(value)
    Err(error)
}

脱糖先(自動生成コード)

// 1. 名前空間static box
static box Result {
    // 2. Schema登録
    _schema: EnumSchemaBox

    _init_schema() {
        me._schema = new EnumSchemaBox()
        me._schema.name = "Result"
        me._schema.register_variant("Ok", ["value"])
        me._schema.register_variant("Err", ["error"])
    }

    // 3. コンストラクタ関数
    Ok(value) {
        local v = new VariantBox("Ok")
        v.fields.push(value)
        return v
    }

    Err(error) {
        local v = new VariantBox("Err")
        v.fields.push(error)
        return v
    }

    // 4. 判定ヘルパー
    is_ok(r: VariantBox) -> BoolBox {
        return r.is_tag("Ok")
    }

    is_err(r: VariantBox) -> BoolBox {
        return r.is_tag("Err")
    }

    // 5. アクセサ(型安全な値取り出し)
    as_ok(r: VariantBox) -> Box {
        if !me.is_ok(r) {
            panic("Expected Ok, got " + r.tag)
        }
        return r.field(0)
    }

    as_err(r: VariantBox) -> Box {
        if !me.is_err(r) {
            panic("Expected Err, got " + r.tag)
        }
        return r.field(0)
    }
}

// 初期化(モジュールロード時に実行)
Result._init_schema()

生成コード量: 約40-60行/enum2 Variant × 20-30行


🔄 @matchマクロ脱糖

入力構文

flow handle_result(r: VariantBox) {
    @match r {
        Ok(v) => {
            console.log("Success: " + v)
            return v
        }
        Err(e) => {
            console.error("Error: " + e)
            return null
        }
    }
}

脱糖先

flow handle_result(r: VariantBox) {
    if r.is_tag("Ok") {
        local v = r.field(0)
        console.log("Success: " + v)
        return v
    } else if r.is_tag("Err") {
        local e = r.field(0)
        console.error("Error: " + e)
        return null
    } else {
        panic("Non-exhaustive match on Result: unexpected variant " + r.tag)
    }
}

脱糖ルール:

  1. パターン解析: Ok(v) → タグ"Ok" + 変数束縛v = r.field(0)
  2. 分岐生成: if ... else if ... else panic(...)
  3. 網羅性チェック: else節で実行時エラー

🎨 使用例

Option型

@enum Option {
    Some(value)
    None
}

// 使用例
flow find_user(id: I64Box) -> VariantBox {
    if id > 0 {
        return Option.Some("User" + id.to_s())
    } else {
        return Option.None()
    }
}

flow main() {
    local result = find_user(42)

    @match result {
        Some(name) => print("Found: " + name)
        None       => print("Not found")
    }
}

AST表現

@enum Expr {
    Lit(value: NumberBox)
    Var(name: StringBox)
    Add(lhs: Expr, rhs: Expr)
}

flow eval(e: Expr, env: MapBox) -> NumberBox {
    @match e {
        Lit(n)      => return n
        Var(name)   => return env.get(name)
        Add(lhs, rhs) => return eval(lhs, env) + eval(rhs, env)
    }
}

🆚 代替案との比較

MapBox vs VariantBox

要素 MapBox VariantBox 判定
決定性 順序不定 配列順序保証 VariantBox勝利
可読性 ⚠️ {tag: "Ok", value: 42} Ok(42) VariantBox勝利
性能 ハッシュ計算 配列アクセス VariantBox勝利
既存実装 実装済み 新規実装 MapBox勝利

結論: VariantBoxが技術的に優位

Box継承 vs VariantBox

要素 Box継承 VariantBox 判定
型安全性 開いた型 閉じた型 VariantBox勝利
網羅性チェック 不可能 可能 VariantBox勝利
既存機能 マクロ不要 マクロ必要 Box継承勝利

結論: VariantBoxが型安全性で優位

MIR拡張 vs マクロ脱糖

要素 MIR拡張 マクロ脱糖 判定
性能 最速 ⚠️ やや遅い MIR拡張勝利
MIR複雑化 16→19-20命令 16命令維持 マクロ勝利
実装コスト 3バックエンド改修 マクロ層のみ マクロ勝利

結論: マクロ脱糖が哲学整合・実装コストで優位


📊 段階的導入戦略

MVP版Phase 20.6: 手動構築

// マクロなし、手動でVariantBox構築
local some = new VariantBox("Some")
some.fields.push(42)

if some.is_tag("Some") {
    print(some.field(0))  // → 42
}

利点: 即座に実装可能、概念検証

v1.0版Phase 20.7: @enumマクロ

@enum Option {
    Some(value)
    None
}

local some = Option.Some(42)
if Option.is_some(some) {
    print(Option.as_some(some))  // → 42
}

利点: コンストラクタ自動生成、可読性向上

v2.0版Phase 20.8: @matchマクロ

@match some {
    Some(v) => print(v)
    None    => print("empty")
}

利点: パターンマッチング、網羅性チェック


🚀 将来拡張

Phase 25: SymbolBoxタグ化

// StringBox → SymbolBox
box VariantBox {
    tag: SymbolBox  // :Some, :None (インターン済み)
    ...
}

利点: パフォーマンス向上、メモリ削減

Phase 25: 静的網羅性チェック

// コンパイル時エラー
@match result {
    Ok(v) => ...
    // エラー: Err caseが不足
}

実装: 型パスPhase 25で実現

Phase 30: GADT拡張

@enum Expr {
    Lit(n: NumberBox) -> Expr
    Add(lhs: Expr, rhs: Expr) -> Expr
}

利点: 型安全性さらに向上


🎓 設計原則まとめ

採用された設計

  1. VariantBoxtag: StringBox, fields: ArrayBox
  2. @enum/@matchマクロ脱糖
  3. 段階的導入MVP→v1.0→v2.0
  4. StringBoxタグSymbolBoxは後回し

棄却された設計

  1. MapBoxベース決定性低い
  2. MIR命令拡張MIR16維持に反する
  3. 一括導入(リスク高い)

成功の鍵

  • 既存マクロ基盤の活用pattern.rs、engine.rs
  • セルフホスト完了後の実装Phase 15.7の後)
  • 80/20ルール適用MVP版で80%の価値)

承認: Phase 15.7完了後に実装開始 優先度: Phase 20内でP2推奨、但しP1マクロ基盤の後