- After inserting Phi at loop header, update variable_map so that subsequent uses (after the loop) refer to the Phi result instead of the latch/body value. This fixes dominance issues in verifier. - Add tests: loop phi normalization and loop+nested-if phi; both pass.
16 KiB
16 KiB
Nyash統一Box設計の深い分析と今後の方向性
Status: Research Created: 2025-08-25 Priority: High Related: Everything is Box哲学の実装レベルでの完全実現
現状の統一Box設計
3種類のBoxの存在
- ビルトインBox - Rustで実装(StringBox, IntegerBox, ConsoleBox等)
- プラグインBox - 動的ライブラリで提供(FileBox等の置き換え可能)
- ユーザー定義Box - Nyashコードで定義(box Person等)
現在の統一アーキテクチャ
UnifiedBoxRegistry(統一レジストリ)
├── BuiltinBoxFactory(優先度1)
├── UserDefinedBoxFactory(優先度2)
└── PluginBoxFactory(優先度3)
統一の美しさ
-
透過的な置き換え
- 同じ名前のBoxをプラグインで上書き可能
- 実行時の動的切り替えも可能
-
統一インターフェース
pub trait BoxFactory: Send + Sync { fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, RuntimeError>; fn box_types(&self) -> Vec<&str>; fn supports_birth(&self) -> bool; } -
優先順位システム
- ビルトイン > ユーザー定義 > プラグイン
- 予約語保護(StringBox等は上書き不可)
InstanceBoxによる完全統一
統一実装の核心
pub struct InstanceBox {
pub class_name: String, // "StringBox", "Person"等
pub inner_content: Option<Box<dyn NyashBox>>, // 内包Box(統一!)
pub fields_ng: Arc<Mutex<HashMap<String, NyashValue>>>,
pub methods: Arc<HashMap<String, ASTNode>>,
}
3つの形態を統一的に扱う
// ビルトイン
InstanceBox::from_any_box("StringBox", Box::new(StringBox::new("hello")))
// プラグイン
InstanceBox::from_any_box("FileBox", plugin_loader.create_box("FileBox"))
// ユーザー定義
InstanceBox::from_declaration("Person", vec!["name", "age"], methods)
基本メソッドの統一提案
問題点
toString()→to_string_box()(Rustの命名規則で異なる)type()→type_name()(微妙に異なる)- 各Boxで個別実装されていて統一感がない
解決案:NyashBoxトレイトにデフォルト実装
pub trait NyashBox: BoxCore + Debug {
// Nyash標準メソッド(デフォルト実装)
fn toString(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(&self.to_string_box().value))
}
fn type(&self) -> Box<dyn NyashBox> {
Box::new(StringBox::new(self.type_name()))
}
fn equals(&self, other: Box<dyn NyashBox>) -> Box<dyn NyashBox> {
Box::new(BoolBox::new(self.equals_internal(other.as_ref())))
}
fn clone(&self) -> Box<dyn NyashBox> {
self.clone_box()
}
}
VMでの統一的な呼び出し
pub(super) fn call_box_method(&self, box_value: Box<dyn NyashBox>, method: &str, args: Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, VMError> {
// 基本メソッドは全Boxで使える!
match method {
"toString" => Ok(box_value.toString()),
"type" => Ok(box_value.type()),
"equals" => Ok(box_value.equals(args[0].clone_or_share())),
"clone" => Ok(box_value.clone()),
_ => self.call_specific_method(box_value, method, args)
}
}
Gemini先生からの提案(部分)
1. NyashValue Enumの導入
pub enum NyashValue {
// 即値(スタック上)
Void,
Bool(bool),
Integer(i64),
Float(f64),
// ヒープ上の不変値
String(Arc<String>),
// 複雑なオブジェクト
Object(Arc<NyashObject>),
}
メリット:
- 小さな値のヒープアロケーション回避
- パターンマッチによる高速ディスパッチ
- 型安全性の向上
2. トレイトの階層化
// 基本トレイト
pub trait NyashBox: Send + Sync + Debug {
fn to_string(&self) -> String;
fn type_name(&self) -> &'static str;
fn equals(&self, other: &dyn NyashBox) -> bool;
}
// 拡張トレイト
pub trait Comparable: NyashBox {
fn compare(&self, other: &dyn NyashBox) -> Option<Ordering>;
}
pub trait Arithmetic: NyashBox {
fn add(&self, other: &dyn NyashBox) -> Result<Box<dyn NyashBox>, String>;
}
3. メタプログラミング機能
pub trait BoxMetadata {
fn on_method_missing(&self, name: &str, args: &[NyashValue]) -> Option<NyashValue>;
fn on_field_access(&self, name: &str) -> Option<NyashValue>;
}
統一継承の実現
現在の課題 → 2025-08-25更新:すべて実装済み!
ビルトインBoxの継承ができない→ ✅ 実装済み!プラグインBoxの継承も未実装→ ✅ 実装済み!
理想的な統一継承(すでに実現!)
// すべて可能になった!
box MyString from StringBox { } // ビルトイン継承 ✅
box MyFile from FileBox { } // プラグイン継承 ✅
box Employee from Person { } // ユーザー定義継承 ✅
// 多重デリゲーションも可能!
box MultiChild from StringBox, IntegerBox { } // ✅
実際のコード例(動作確認済み)
box EnhancedString from StringBox {
init { prefix, suffix }
birth(text) {
from StringBox.birth(text) // 透過的にpackに変換される
me.prefix = "【"
me.suffix = "】"
}
enhanced() {
return me.prefix + me.toString() + me.suffix + "✨"
}
}
さらなる美化への道
1. パイプライン演算子
// 現在
local result = str.substring(0, 5).toUpperCase().trim()
// パイプライン版
local result = str
|> substring(0, 5)
|> toUpperCase()
|> trim()
2. Box階層の整理
NyashBox (trait)
├── ValueBox (数値・文字列・真偽値)
│ ├── IntegerBox
│ ├── StringBox
│ └── BoolBox
├── ContainerBox (コレクション)
│ ├── ArrayBox
│ └── MapBox
├── IOBox (入出力)
│ ├── ConsoleBox
│ └── FileBox
└── ConcurrentBox (並行処理)
├── FutureBox
└── ChannelBox
3. エフェクトシステムの美化
impl ArrayBox {
#[effect(Pure)]
fn length(&self) -> IntegerBox { }
#[effect(State)]
fn push(&mut self, item: Box<dyn NyashBox>) { }
}
実装優先順位(80/20ルール)
今すぐやるべき(80%)
- 基本メソッド(toString, type, equals, clone)の統一実装
- VMでの統一的なメソッドディスパッチ
- InstanceBoxのinner_content活用の徹底
後でじっくり(20%)
- NyashValue enum導入によるパフォーマンス最適化
- トレイト階層化による整理
- パイプライン演算子の実装
- メタプログラミング機能
- 完全な統一継承システム
まとめ
現在のNyashの統一Box設計は、すでに相当美しく実装されている。特に:
- UnifiedBoxRegistryによる透過的な管理
- 優先順位システムによる明確な解決
- InstanceBoxによる統一的な扱い
これらは「Everything is Box」哲学を実装レベルで体現している。
ユーザー定義Boxをビルトイン/プラグインBoxと完全に同じレベルで扱うことは、強引ではなく、むしろ設計として自然で美しい。この統一により、言語の一貫性と拡張性が大幅に向上する。
今後は、基本メソッドの統一実装から始めて、段階的により洗練された設計へと進化させていくのが良いだろう。
🚀 MIR/VM統一実装計画(2025-08-25追記)
📍 現状の課題
VMとMIRで、Box型によって異なる処理をしている:
// VMでの現状:InstanceBoxだけ特別扱い
if let Some(inst) = arc_box.as_any().downcast_ref::<InstanceBox>() {
// ユーザー定義Box → 関数呼び出しに変換
let func_name = format!("{}.{}/{}", inst.class_name, method, args.len());
} else {
// ビルトイン/プラグイン → 直接メソッド呼び出し
self.call_box_method(cloned_box, method, nyash_args)?
}
🎯 統一実装の提案
1. 統一メソッドディスパッチインターフェース
pub trait UnifiedBox: NyashBox {
fn dispatch_method(&self, method: &str, args: Vec<Box<dyn NyashBox>>)
-> Result<Box<dyn NyashBox>, String> {
// デフォルト実装:既存のメソッド呼び出しを使用
self.call_method(method, args)
}
}
// InstanceBoxでのオーバーライド
impl UnifiedBox for InstanceBox {
fn dispatch_method(&self, method: &str, args: Vec<Box<dyn NyashBox>>)
-> Result<Box<dyn NyashBox>, String> {
// MIR関数へのリダイレクト
let func_name = format!("{}.{}/{}", self.class_name, method, args.len());
// VM経由で関数呼び出し
}
}
2. VMの簡素化
// 統一後:すべて同じ処理パス
let result = match &recv {
VMValue::BoxRef(arc_box) => {
arc_box.dispatch_method(method, nyash_args)?
}
_ => {
recv.to_nyash_box().dispatch_method(method, nyash_args)?
}
};
3. MIRレベルでの統一
BoxCall命令ですべてのBox型を統一的に処理- 型による分岐や特殊処理を削除
- コンパイル時の最適化は維持
💎 期待される効果
-
コードの簡素化
- VM内の条件分岐削除で30%以上のコード削減
- 新Box型追加時の変更箇所が最小限に
-
保守性の向上
- Box型の実装詳細がVMから隠蔽される
- テストが書きやすくなる
-
パフォーマンス
- 統一的な最適化(メソッドキャッシュ等)が可能
- 仮想関数テーブルによる高速化の可能性
-
美しさ
- 「Everything is Box」が実装レベルでも完全に実現
- シンプルで理解しやすいコード
📅 実装ロードマップ
- Phase 1: UnifiedBoxトレイトの導入(後方互換性を保ちながら)
- Phase 2: VMでの統一ディスパッチ実装
- Phase 3: MIRビルダーの簡素化
- Phase 4: 旧実装の削除とクリーンアップ
🌟 最終的なビジョン
すべてのBox(ビルトイン、プラグイン、ユーザー定義)が完全に統一された世界:
- 同じインターフェース
- 同じ実行パス
- 同じ最適化機会
これこそが「Everything is Box」の究極の実現!
🔌 プラグインローダーv2との統合
現在のプラグインシステムとの関係
- プラグインローダーv2がすでに統一的なインターフェースを提供
extern_call経由での統一的なアクセス- UnifiedBoxトレイトとの相性は良好
統合のメリット
- プラグインBoxも
dispatch_method()で統一処理 - ホットリロード時も透過的に動作
- FFI境界を意識しない実装
📊 パフォーマンス測定計画
現在のベースライン
- インタープリター基準で13.5倍高速化達成(VM実装)
- BoxCall命令の実行時間が全体の約30%
統一実装後の予測
- 条件分岐削減で5-10%の高速化期待
- メソッドキャッシュで追加20%改善の可能性
- 測定方法:
--benchmark --iterations 1000で検証
🔄 移行時の互換性戦略
段階的移行計画
- Phase 1: UnifiedBoxトレイトを追加(既存APIは維持)
- Phase 2: 警告付きで旧API使用を通知
- Phase 3: 内部実装を統一版に切り替え
- Phase 4: 旧APIをdeprecated化
- Phase 5: 完全削除(6ヶ月後)
テスト戦略
- 既存の全E2Eテストが通ることを保証
- パフォーマンスリグレッションテスト追加
- プラグイン互換性テストスイート
⚡ JITコンパイラとの統合(Phase 9準備)
統一メソッドディスパッチの利点
- JITが最適化しやすい単純な呼び出しパターン
- インライン展開の機会増加
- 型情報を活用した特殊化
仮想関数テーブル(vtable)戦略
struct BoxVTable {
methods: HashMap<String, fn(&dyn NyashBox, Vec<Box<dyn NyashBox>>) -> Result<Box<dyn NyashBox>, String>>,
}
- 起動時に事前計算
- JITコンパイル時に直接参照
- キャッシュフレンドリーな配置
🔧 具体的な実装タスク(TODO)
Phase 1: 基礎実装(1週間)
- UnifiedBoxトレイトの定義(src/box_trait.rs)
- StringBox, IntegerBox等への実装
- InstanceBoxへのdispatch_method実装
- 単体テストの作成
Phase 2: VM統合(2週間)
- execute_boxcall()の簡素化
- InstanceBox特別扱いコードの削除
- VMValueとの統合
- E2Eテスト全パス確認
- ベンチマーク実行と比較
Phase 3: 最適化(1週間)
- メソッドキャッシュ実装
- 頻出メソッドの特殊化
- vtable事前計算
- JIT統合準備
Phase 4: クリーンアップ(3日)
- 旧実装コードの削除
- ドキュメント更新
- CHANGELOG記載
- マイグレーションガイド作成
検証項目
- 全Box型でtoString/type/equals/cloneが動作
- プラグインBoxの透過的な動作
- パフォーマンス改善の確認
- メモリ使用量の変化なし
🚀 究極の統一:ビルトインBox完全プラグイン化構想
現状の二重実装問題
- plugin_loader.rs (1217行) - ビルトインBoxの動的ライブラリ化
- plugin_loader_v2.rs (906行) - プラグインBoxシステム
- 合計2000行以上の重複!
完全プラグイン化の提案
すべてをプラグインに統一
// 現在
ビルトインFileBox → 静的リンク
プラグインFileBox → 動的ロード(.so)
// 統一後
すべてのBox → プラグイン(.so)として実装
コアBoxの自動ロード戦略
const CORE_BOXES: &[&str] = &[
"libnyash_string_box.so", // StringBox(必須)
"libnyash_integer_box.so", // IntegerBox(必須)
"libnyash_bool_box.so", // BoolBox(必須)
"libnyash_console_box.so", // ConsoleBox(print用)
];
// 起動時に自動ロード
fn init_core_boxes() {
for plugin in CORE_BOXES {
plugin_loader.load_required(plugin)
.expect("Core box loading failed");
}
}
メリット
- コード削減: plugin_loader.rs (1217行) を完全削除
- 統一性: Everything is Boxの究極の実現
- 柔軟性: StringBoxすら置き換え可能
- ビルド高速化: 本体が軽量に
- 配布の柔軟性: 必要なBoxだけ選択可能
考慮事項
パフォーマンス
- FFI境界のオーバーヘッドはナノ秒レベル
- 実用上の影響なし
デバッグの課題と対策
// 課題:エラー時のスタックトレース
thread 'main' panicked at 'FFI boundary: 0x7f8b2c001234'
// 対策1:プラグイン側でのロギング
#[no_mangle]
pub extern "C" fn box_method_toString() {
eprintln!("[StringBox::toString] called from {:?}", std::thread::current().id());
}
// 対策2:デバッグシンボル保持
cargo build --features debug-symbols
// 対策3:プラグイン単体テストの充実
#[test]
fn test_string_box_methods() { /* ... */ }
実装ロードマップ
- Phase A: コアBoxのプラグイン化
- StringBox, IntegerBox, BoolBox, ConsoleBox
- Phase B: 起動時自動ロード機構
- Phase C: plugin_loader.rs削除
- Phase D: ドキュメント・テスト整備
設定ファイル案
# ~/.nyash/config.toml
[plugins]
core_path = "./plugins/core/"
search_paths = ["./plugins", "/usr/lib/nyash/plugins"]
[core_boxes]
required = ["string", "integer", "bool", "console"]
optional = ["file", "math", "time"]
これにより、「Everything is Box」哲学が実装レベルでも完全に実現される!