feat(phase-9.75g-0): Implement BID-FFI Day 3 - Box type integration

- Implement BID Box Bridge interface for Nyash Box <-> BID Handle conversion
- Add StringBox BID bridge implementation with handle/TLV support
- Add IntegerBox BID bridge implementation with handle/TLV support
- Implement BoxRegistry for managing Box instances and handles
- Add comprehensive tests for StringBox/IntegerBox BID round-trip
- Extract helper functions for string/integer value extraction

Everything is Box philosophy shines through unified BID integration! 🎉

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-17 19:54:57 +09:00
parent 53f20464b6
commit a5ff3ecafe
9 changed files with 1118 additions and 0 deletions

View File

@ -0,0 +1,179 @@
# ChatGPT先生による箱理論設計 最終レビュー結果 (2025-08-17)
## 🎯 総合評価
### ✅ **Executive SummaryChatGPT判定**
> **方向性は正しい**: primitives-by-value + box-by-handle は適切で、Everything is Box哲学を維持している。
> **1週間Phase 1は現実的**(スコープを限定すれば)
## 🔧 重要な修正提案
### 1. **Handle設計の改善** 🚨
```rust
// ❌ 現在の設計
Handle(String) // "StringBox:123" - 文字列解析コスト高い
// ✅ ChatGPT推奨
Handle {
type_id: u32, // StringBox=1, FileBox=6等
instance_id: u32 // インスタンス識別子
}
// または単一u64として: type_id << 32 | instance_id
```
**理由**: 文字列解析は遅く、エラーの原因。バイナリ形式が効率的。
### 2. **メタデータAPI追加** 💡
```c
// プラグインに追加すべき関数
u32 nyash_plugin_abi(void); // ABI版本(1)を返す
i32 nyash_plugin_init(const NyashHostVtable*, NyashPluginInfo*);
void nyash_plugin_shutdown(void);
```
**理由**: バージョン管理、ホスト連携、型・メソッド登録が必要。
### 3. **TLV統一フォーマット** 📦
```c
// BID-1 TLV仕様ChatGPT提案
struct BidTLV {
u16 version; // 1
u16 argc; // 引数数
// TLVs: u8 tag, u8 reserved, u16 size, payload
}
// タグ定義
1=Bool(1), 2=I32(4), 3=I64(8), 4=F32(4), 5=F64(8),
6=String(utf8), 7=Bytes, 8=Handle(8 bytes)
// 予約: 20=Result, 21=Option, 22=Array (Phase 2)
```
**理由**: メソッドごとの個別エンコードを避け、統一フォーマットで効率化。
### 4. **メモリ管理の明確化** 🛠️
```c
// ChatGPT推奨: 2回呼び出しパターン
// 1回目: result_ptr=null でサイズ取得
// 2回目: ホストがallocateして再呼び出し
i32 nyash_plugin_invoke(..., result_ptr, result_len);
```
**理由**: 所有権が明確、メモリリーク回避。
## 📊 各質問への回答
### 1. 箱理論の技術的妥当性 ✅
- **適切性**: 全Boxをハンドル統一は妥当
- **統一扱い**: 既存/プラグインを同一レジストリで管理可
- **ハンドル表現**: バイナリ形式(type_id, instance_id)に変更推奨
### 2. 最低設計のメリット・デメリット ✅
- **メリット**: 実装最短、既存Box再利用最大化、API安定
- **デメリット**: Array/Map未対応で複合データが冗長TLVで緩和可
- **戦略**: Phase 1基本 → Phase 2拡張は正解
### 3. 既存資産活用の是非 ✅
- **FutureBox再利用**: 正解、二重実装回避
- **統合アプローチ**: 適切、メソッドIDはメタデータで合意
- **純粋性トレードオフ**: 実用性を優先が現実的
### 4. 実装現実性 ✅
- **1週間**: 現実的(スコープ限定時)
- **統合難易度**: 中レベル、FutureBoxのwake統合がポイント
- **Linux x86-64限定**: 妥当
### 5. 将来拡張性 ✅
- **gRPC/REST**: invoke+TLVをRPCカプセル化で対応可
- **Transport抽象化**: Phase 2でTransportBox導入
- **P2P**: 同じinvokeメッセージで転送可能
## 🔧 具体的な実装修正案
### BidType修正版
```rust
#[derive(Clone, Debug, PartialEq)]
pub enum BidType {
// プリミティブ(値渡し)
Bool, I32, I64, F32, F64, String, Bytes,
// Box参照ハンドル
Handle { type_id: u32, instance_id: u32 },
// メタ型
Void,
// Phase 2予約TLVタグ予約済み
Option(Box<BidType>), // tag=21
Result(Box<BidType>, Box<BidType>), // tag=20
Array(Box<BidType>), // tag=22
}
```
### C ABI修正版
```c
// メタデータ構造体
typedef struct {
u32 type_id;
const char* type_name;
u32 method_count;
// メソッドテーブル...
} NyashPluginInfo;
// ホスト機能
typedef struct {
void* (*alloc)(size_t size);
void (*free)(void* ptr);
void (*wake)(u32 future_id); // FutureBox起床
void (*log)(const char* msg);
} NyashHostVtable;
// プラグインAPI
u32 nyash_plugin_abi(void);
i32 nyash_plugin_init(const NyashHostVtable* host, NyashPluginInfo* info);
i32 nyash_plugin_invoke(u32 type_id, u32 method_id, u32 instance_id,
const u8* args, size_t args_len,
u8* result, size_t* result_len);
void nyash_plugin_shutdown(void);
```
## ⚠️ リスク対策
### ChatGPT指摘のリスク
1. **ハンドル再利用**: generation追加で回避
2. **スレッド前提**: シングルスレッド前提を明記
3. **メソッドID衝突**: ビルド時固定で回避
4. **エラー伝播**: トランスポート/ドメインエラー分離
5. **文字列エンコード**: UTF-8必須、内部NUL禁止
## 📋 Phase 1実装チェックリストChatGPT提案
- [ ] BID-1 TLV仕様とエラーコード定義
- [ ] ホストレジストリ + Handle{type_id,instance_id}
- [ ] プラグインinit/abi/shutdown追加
- [ ] 既存StringBox/IntegerBox/FutureBoxブリッジ
- [ ] FileBoxプラグインopen/read/close
- [ ] FutureBox用wake経路
- [ ] 適合性テスト(プリミティブ、ハンドル、エラー)
## 🚀 結論
ChatGPT先生の判定
> **箱理論設計は技術的に妥当!** ただし具体的な実装詳細で重要な改善提案あり。
### 主要な価値
1. **Everything is Box哲学の技術的実現**を評価
2. **具体的で実装可能な修正案**を提示
3. **1週間実装の現実性**を確認
4. **将来拡張への明確な道筋**を提示
### 推奨アクション
1. Handle設計をバイナリ形式に変更
2. メタデータAPIを追加
3. TLV統一フォーマット導入
4. Phase 1スコープでの実装開始
---
**レビュー日**: 2025-08-17
**レビュワー**: ChatGPT-5
**結論**: 方向性正しい、実装詳細要修正、1週間実装可能

View File

@ -0,0 +1,248 @@
# Phase 9.75g-0 修正版: Everything is Box哲学準拠のシンプル設計
## 🎯 基本方針Nyash哲学ファースト
**重要な気づき**: ChatGPT設計は一般的だが、**既存のFutureBox等と二重実装**になってしまう。
Nyashの「Everything is Box」哲学を貫き、既存資産を活用する。
## 🌟 Nyash哲学に準拠した設計
### 1. 型システム:プリミティブ + Handle
```rust
// src/bid/types.rs - Everything is Box哲学準拠
#[derive(Clone, Debug, PartialEq)]
pub enum BidType {
// === プリミティブ型FFI境界で直接渡せる ===
Bool, // Nyashのbool literal
I32, // 32ビット整数
I64, // Nyashの標準整数
F32, // 32ビット浮動小数点
F64, // Nyashの標準浮動小数点
String, // UTF-8文字列 (ptr: usize, len: usize)
Bytes, // バイナリデータ (ptr: usize, len: usize)
// === Everything is Box: すべてのBoxは統一Handle ===
Handle(String), // "StringBox:123", "FileBox:456", "FutureBox:789"
// === メタ型FFI用 ===
Void, // 戻り値なし
// Phase 2以降で追加定義だけ先に
Option(Box<BidType>), // Option<T>
Result(Box<BidType>, Box<BidType>), // Result<T, E>
Array(Box<BidType>), // Array<T>
// === Everything is Box哲学の拡張 ===
// Array, Map, Future等はすべてHandle("ArrayBox:id")として扱う
}
// Nyashの既存Boxとの対応表
/*
Handle("StringBox:123") → StringBox インスタンス
Handle("IntegerBox:456") → IntegerBox インスタンス
Handle("FutureBox:789") → FutureBox インスタンス(非同期)
Handle("FileBox:101") → FileBox インスタンス
Handle("ArrayBox:102") → ArrayBox インスタンス
Handle("P2PBox:103") → P2PBox インスタンス
*/
```
### 2. シンプルなBoxヘッダーNyash統一仕様
```rust
// 既存のNyash Boxヘッダーと統一
#[repr(C, align(8))]
pub struct BoxHeader {
magic: u32, // "NYBX" (0x5859424E)
version: u16, // 1
_pad: u16, // アライメント用
type_id: u32, // BoxTypeIdStringBox=1, FileBox=2等
instance_id: u32, // インスタンス識別子
ref_count: u32, // 非atomicNyashはシングルスレッド中心
flags: u32, // 将来の拡張用
}
// Nyashの既存Box型との統合
pub const NYASH_BOX_TYPES: &[(u32, &str)] = &[
(1, "StringBox"),
(2, "IntegerBox"),
(3, "BoolBox"),
(4, "ArrayBox"),
(5, "MapBox"),
(6, "FileBox"), // プラグインで提供
(7, "FutureBox"), // 既存の非同期Box
(8, "P2PBox"), // 既存のP2P Box
// 新しいプラグインBoxも同じ仕組みで追加
];
```
### 3. 単一エントリーポイントEverything is Box対応
```rust
// Nyashの全Boxを統一的に扱える設計
#[no_mangle]
extern "C" fn nyash_plugin_invoke(
box_type_id: u32, // どのBox型StringBox=1, FileBox=6等
method_id: u32, // どのメソッドopen=1, read=2等
instance_id: u32, // どのインスタンスHandle解析用
args_ptr: *const u8, // 引数データ
args_len: usize, // 引数サイズ
result_ptr: *mut u8, // 結果置き場
result_len: *mut usize, // 結果サイズ
) -> i32 { // 0=成功, 非0=エラー
// Everything is Box哲学すべて同じ仕組みで処理
}
// 既存のNyash Boxとの統合例
fn handle_stringbox_call(method_id: u32, instance_id: u32, args: &[u8]) -> Result<Vec<u8>, BidError> {
match method_id {
1 => { /* length() */ },
2 => { /* substring() */ },
3 => { /* append() */ },
_ => Err(BidError::MethodNotFound),
}
}
fn handle_filebox_call(method_id: u32, instance_id: u32, args: &[u8]) -> Result<Vec<u8>, BidError> {
match method_id {
1 => { /* open() */ },
2 => { /* read() */ },
3 => { /* write() */ },
4 => { /* close() */ },
_ => Err(BidError::MethodNotFound),
}
}
```
### 4. 既存Nyash Boxとの統合戦略
```rust
// src/bid/integration.rs - 既存Boxとの橋渡し
pub struct NyashBoxRegistry {
// 既存のStringBox、ArrayBox等のインスタンス管理
instances: HashMap<u32, Arc<RwLock<dyn NyashBox>>>,
next_id: AtomicU32,
}
impl NyashBoxRegistry {
pub fn register_box(&self, box_instance: Arc<RwLock<dyn NyashBox>>) -> u32 {
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
self.instances.insert(id, box_instance);
id
}
pub fn call_method(&self, type_id: u32, instance_id: u32, method_id: u32, args: &[u8])
-> Result<Vec<u8>, BidError>
{
match type_id {
1 => self.call_stringbox_method(instance_id, method_id, args),
6 => self.call_filebox_method(instance_id, method_id, args),
7 => self.call_futurebox_method(instance_id, method_id, args), // 既存FutureBox活用
_ => Err(BidError::UnknownBoxType(type_id)),
}
}
}
```
## 📋 修正された実装計画
### Day 1: Nyash統合基盤
- [ ] `src/bid/types.rs` - Everything is Box準拠型定義
- [ ] `src/bid/registry.rs` - 既存Box統合レジストリ
- [ ] `src/bid/header.rs` - 統一Boxヘッダー
- [ ] テスト: 既存StringBoxとの統合
### Day 2: プラグインローダー
- [ ] `src/bid/loader.rs` - dlopen/dlsym
- [ ] 最小プラグインMathBox拡張
- [ ] テスト: 既存BoxとプラグインBoxの共存
### Day 3: Handle型統合
- [ ] Handle("FileBox:123")の解決機構
- [ ] プリミティブ⇔Handle変換
- [ ] テスト: 全Box型の統一的操作
### Day 4: FileBox実装
- [ ] FileBoxプラグインの完全実装
- [ ] 既存のNyashコードとの互換性
- [ ] テスト: FileBox e2e動作
### Day 5: エラー処理とOption/Result
- [ ] 統一エラーシステム
- [ ] Option/Result型の最小実装
- [ ] テスト: エラーケース網羅
### Day 6-7: 統合テスト・ドキュメント
- [ ] 既存インタープリターとの統合
- [ ] 使用例とドキュメント
- [ ] Linux x86-64 CI設定
## 🌟 この設計の哲学的利点
### 1. Everything is Box哲学の完全準拠
```nyash
// Nyashコード側変わらない
local file = new FileBox("test.txt", "r") // プラグイン提供
local future = new FutureBox() // 既存Box
local array = new ArrayBox() // 既存Box
// すべて同じHandle("BoxType:id")として扱われる
```
### 2. 既存資産の完全活用
- ❌ 新しいBidFuture実装 → ✅ 既存FutureBox活用
- ❌ 新しい型システム → ✅ 既存Nyash型との統合
- ❌ 二重実装 → ✅ 単一の統一システム
### 3. スレッド最小設計
```rust
// Nyashの現実に合わせた設計
ref_count: u32, // 非atomicシングルスレッド中心
// Phase 2以降でatomic対応を検討
#[cfg(feature = "atomic")]
ref_count: AtomicU32,
```
### 4. エラー対策の強化
```rust
// 統一エラー処理でプラグインの安定性向上
pub enum BidError {
UnknownBoxType(u32),
InstanceNotFound(u32),
MethodNotFound(u32),
InvalidArguments(String),
PluginError(String), // プラグイン側エラーを安全に伝播
}
```
## ✅ 成功基準Everything is Box準拠
### 必須
- [ ] 既存StringBox、ArrayBoxとプラグインFileBoxが同じ仕組みで動作
- [ ] Handle("FileBox:123")でのBox操作
- [ ] 既存FutureBoxの活用新実装なし
- [ ] すべてのBoxが統一的にアクセス可能
### 理想
- [ ] 新しいプラグインBoxも既存Boxと見分けがつかない
- [ ] Nyashコード側は変更不要
- [ ] Everything is Box哲学の技術的実現
## 📝 まとめ
**ChatGPT先生の一般論は正しいが、Nyashの独特な哲学には合わない。**
**Nyash Way**: Everything is Box → すべてHandle + 既存Box活用
**一般的Way**: 型システム分離 → 新しい実装追加
**結論**: Nyashの哲学を貫いて、既存の資産を最大活用する設計で進む
---
**修正日**: 2025-08-17
**修正理由**: Everything is Box哲学の完全準拠、既存資産活用
**キーワード**: Simple, Nyash-native, No-duplication

View File

@ -0,0 +1,231 @@
# Phase 9.75g-0 最終修正版: ChatGPT先生の知恵を反映した型設計
## 🎯 ChatGPT先生の明確な判断
> **結論**: Future/StreamはBidType値型に含めないでください。非同期性は「実行モデル」であって「値の表現」ではありません。
## 🛠️ 修正された型システム設計
### 1. 値型BidType- 純粋な値のみ
```rust
// src/bid/types.rs - ChatGPT先生推奨の清潔な設計
#[derive(Clone, Debug, PartialEq)]
pub enum BidType {
// === 基本型Phase 1で実装 ===
Bool,
I32,
I64,
F32,
F64,
String, // (ptr: usize, len: usize)
Bytes, // (ptr: usize, len: usize)
// === 複合型Phase 2で実装 ===
Array(Box<BidType>), // 配列
List(Box<BidType>), // 可変長リスト
Map(Box<BidType>, Box<BidType>), // キーバリューマップ
Tuple(Vec<BidType>), // タプル
Record(Vec<(String, BidType)>), // 名前付きフィールド
Variant(Vec<(String, Option<BidType>)>), // 列挙型
// === 特殊型Phase 2で実装 ===
Option(Box<BidType>), // null許容
Result(Box<BidType>, Box<BidType>), // エラー型
Handle(String), // 不透明ハンドル(同期リソース用)
Void, // 戻り値なし
// === 拡張用(定義だけ) ===
Opaque(String), // 不透明型
// ❌ 削除: Future/Streamは値型ではない
// Future(Box<BidType>), // 削除
// Stream(Box<BidType>), // 削除
}
```
### 2. 実行モデルMethodShape- 新設計
```rust
// メソッドの実行形状を表現ChatGPT推奨
#[derive(Clone, Debug, PartialEq)]
pub enum MethodShape {
Sync, // 通常の同期呼び出し
Async, // Future<T>を返す(ハンドル経由)
Streaming, // Stream<T>を返す(ハンドル経由)
}
// メソッドシグネチャ(形状と値型を分離)
#[derive(Clone, Debug)]
pub struct MethodSig {
pub name: String,
pub shape: MethodShape, // 実行モデル
pub params: Vec<BidType>, // 引数の値型
pub returns: BidType, // 戻り値の値型Future抜き
pub effects: Vec<Effect>,
}
// BID定義でメソッド記述
#[derive(Clone, Debug)]
pub struct Method {
pub sig: MethodSig,
pub doc: Option<String>,
}
```
### 3. 非同期ハンドルFFI境界用
```rust
// ChatGPT推奨のハンドル方式
use std::ffi::c_void;
// FFI境界での非同期ハンドル不透明ポインタ
#[repr(transparent)]
pub struct BidFutureHandle(*mut c_void);
#[repr(transparent)]
pub struct BidStreamHandle(*mut c_void);
// Rust側の安全ラッパー
pub struct BidFuture {
handle: BidFutureHandle,
return_type: BidType,
}
pub struct BidStream {
handle: BidStreamHandle,
item_type: BidType,
}
// 将来のRust async/await統合
impl std::future::Future for BidFuture {
type Output = Result<BidValue, BidError>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
// FFI経由でpolling or callback設定
unimplemented!("Phase 3で実装")
}
}
impl futures_core::Stream for BidStream {
type Item = Result<BidValue, BidError>;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
unimplemented!("Phase 3で実装")
}
}
```
### 4. Connection trait形状別実装
```rust
// ChatGPT推奨の分離アプローチ
pub trait Connection: Send + Sync {
// 同期呼び出しPhase 1で実装
fn invoke(&self, sig: &MethodSig, args: &[BidValue]) -> Result<BidValue, BidError>;
// 非同期呼び出しPhase 3で実装
fn invoke_future(&self, sig: &MethodSig, args: &[BidValue]) -> Result<BidFuture, BidError> {
Err(BidError::Unsupported("async not supported yet".to_string()))
}
// ストリーミングPhase 3で実装
fn invoke_stream(&self, sig: &MethodSig, args: &[BidValue]) -> Result<BidStream, BidError> {
Err(BidError::Unsupported("streaming not supported yet".to_string()))
}
}
```
### 5. FFI境界の非同期APIPhase 3で実装
```c
// ChatGPT推奨のC ABI設計Phase 3で実装予定
// Future操作
extern "C" fn bid_future_poll(
handle: *mut c_void,
out_value: *mut BidValue,
out_is_ready: *mut bool
) -> BidStatus;
extern "C" fn bid_future_set_callback(
handle: *mut c_void,
callback: extern "C" fn(*mut c_void, BidValue, BidStatus),
user_data: *mut c_void
) -> BidStatus;
extern "C" fn bid_future_cancel(handle: *mut c_void) -> BidStatus;
extern "C" fn bid_future_free(handle: *mut c_void);
// Stream操作
extern "C" fn bid_stream_poll_next(
handle: *mut c_void,
out_item: *mut BidValue,
out_has_item: *mut bool,
out_is_closed: *mut bool
) -> BidStatus;
extern "C" fn bid_stream_set_callback(
handle: *mut c_void,
callback: extern "C" fn(*mut c_void, BidValue, bool, BidStatus),
user_data: *mut c_void
) -> BidStatus;
extern "C" fn bid_stream_close(handle: *mut c_void) -> BidStatus;
extern "C" fn bid_stream_free(handle: *mut c_void);
```
## 📋 修正された実装スケジュール
### Phase 11週間- 同期のみ
```rust
// 実装するもの
- BidType基本型Bool, I32, I64, F32, F64, String
- MethodShape::Syncのみ
- DynamicLibraryコネクター
- Connection::invoke()のみ
// 実装しないもの
- 非同期型(Future/Stream 定義から削除済み
- MethodShape::Async/Streaming unsupportedエラー
```
### Phase 22週間後- 複合型
```rust
// 追加実装
- Array, List, Map, Option, Result型
- エラー処理の充実
- 複数プラグイン同時ロード
```
### Phase 31ヶ月後- 非同期
```rust
// ハンドル方式で非同期追加
- BidFuture/BidStream実装
- FFI境界非同期API
- Rust async/await統合
- WasmComponent対応
```
## 🌟 ChatGPT先生の知恵のまとめ
1. **型と実行モデルの分離** - 値型は純粋に、実行形状は別定義
2. **FFI境界の現実性** - ハンドルAPI関数群で非同期表現
3. **WASM整合性** - Component Modelの流儀に準拠
4. **段階的実装** - unsupportedエラーでpanic回避
5. **将来拡張性** - Transport差異を抽象化で吸収
## ✅ この設計の利点
- **シンプル**: 型システムが明確(値型のみ)
- **拡張可能**: 実行モデルを後から追加可能
- **FFI現実的**: C ABIで実際に渡せる形
- **標準準拠**: WASM Component Modelと整合
- **実装しやすい**: 同期から始めて段階的に
---
**修正日**: 2025-08-17
**修正理由**: ChatGPT先生のアドバイス適用
**重要な変更**: Future/Stream削除、MethodShape導入

View File

@ -0,0 +1,91 @@
Nyash BID-FFI実装の型定義ファースト戦略について、特に非同期型の設計に関してレビューをお願いします。
【現在の設計】
phase_9_75g_0_revised_type_first_approach.mdより抜粋
```rust
pub enum BidType {
// 基本型
Bool, I32, I64, F32, F64, String, Bytes,
// 複合型
Array(Box<BidType>), Map(Box<BidType>, Box<BidType>),
Option(Box<BidType>), Result(Box<BidType>, Box<BidType>),
// === 問題のある部分 ===
Future(Box<BidType>), // 非同期結果
Stream(Box<BidType>), // ストリーム
}
```
【懸念点】
1. **Future/StreamはBidTypeに含めるべきか**
- FFI境界でFutureそのものは渡せない
- C ABIは基本的に同期的
- 非同期は実装の詳細であって値の型ではない?
2. **現在のConnection trait設計**
```rust
trait Connection {
fn invoke(&self, ...) -> Result<BidValue, BidError>;
fn invoke_async(&self, ...) -> Result<FutureHandle, BidError>;
fn stream(&self, ...) -> Result<StreamHandle, BidError>;
}
```
【代替案の検討】
案A: メソッドのシェイプで表現
```rust
pub enum MethodShape {
Sync, // 通常の同期呼び出し
Async, // Future<T>を返す
Stream, // Stream<T>を返す
}
pub struct Method {
pub shape: MethodShape, // ここで同期/非同期を区別
pub returns: BidType, // 実際の値の型Future抜き
}
```
案B: ハンドル型として扱う
```rust
pub enum BidType {
// Future/Stream削除
Handle(String), // "FutureHandle", "StreamHandle"等
}
```
案C: 型階層を分ける
```rust
pub enum BidType { /* 値型のみ */ }
pub enum BidAsyncType {
Future(BidType),
Stream(BidType),
}
```
【質問】
1. **型システム設計**
- Future/StreamをBidTypeに含めるのは適切か
- FFI境界での非同期処理の正しい表現方法は
- 値型と実行モデルを混在させることの是非は?
2. **実装戦略**
- 型定義ファースト戦略(全型を最初に定義)は妥当か?
- unimplemented!()での段階的実装は適切か?
- ビルドエラー回避とAPI安定性のトレードオフは
3. **FFI境界での非同期**
- C ABI経由での非同期処理の標準的な方法は
- ポーリング vs コールバック vs ハンドル方式?
- WASM Component Modelではどう扱われている
4. **将来の拡張性**
- gRPC/RESTでの非同期は別の話
- TransportTypeごとに非同期モデルが異なる場合の抽象化は
- Rust async/awaitとの統合方法は
実装者Rust中級者が混乱しない、シンプルで拡張可能な設計を教えてください。特に「Future/StreamはBidTypeに含めるべきか」の判断をお願いします。

View File

@ -0,0 +1,125 @@
Nyash「箱理論」に基づくBID-FFI設計の最終レビューをお願いします。
【Nyashの箱理論Everything is Box
Nyashプログラミング言語は「Everything is Box」哲学を採用しており、すべてのデータと機能がBoxとして統一されています。
既存のBox型:
- StringBox, IntegerBox, BoolBox基本型
- ArrayBox, MapBoxコレクション
- FutureBox非同期処理
- P2PBoxネットワーク
- FileBox将来プラグインで提供予定
【設計の変遷】
1. 最初の設計: 野心的すぎて複雑gRPC/REST/P2P等全部
2. ChatGPT提案: 一般的で正しいが、既存FutureBoxと二重実装になる
3. 最終設計: 箱理論準拠の最低設計
【最終設計案】
```rust
// 1. 型システム:プリミティブ + Handle統一
pub enum BidType {
// プリミティブFFI境界で直接渡せる
Bool, I32, I64, F32, F64, String, Bytes,
// Everything is Box: すべてのBoxは統一Handle
Handle(String), // "StringBox:123", "FileBox:456", "FutureBox:789"
// メタ型
Void,
// Phase 2以降定義だけ
Option(Box<BidType>),
Result(Box<BidType>, Box<BidType>),
Array(Box<BidType>),
}
// 2. 既存Boxとの対応
/*
Handle("StringBox:123") → 既存StringBox インスタンス
Handle("FileBox:456") → プラグインFileBox
Handle("FutureBox:789") → 既存FutureBox非同期
Handle("P2PBox:101") → 既存P2PBox
*/
// 3. 統一エントリーポイント
extern "C" fn nyash_plugin_invoke(
box_type_id: u32, // StringBox=1, FileBox=6等
method_id: u32, // open=1, read=2等
instance_id: u32, // Handle解析用
args_ptr: *const u8,
args_len: usize,
result_ptr: *mut u8,
result_len: *mut usize,
) -> i32
// 4. Boxヘッダー統一
#[repr(C, align(8))]
pub struct BoxHeader {
magic: u32, // "NYBX"
version: u16,
type_id: u32, // BoxTypeId
instance_id: u32, // インスタンス識別子
ref_count: u32, // 非atomicシングルスレッド中心
flags: u32, // 拡張用
}
// 5. 既存Box統合
pub struct NyashBoxRegistry {
instances: HashMap<u32, Arc<RwLock<dyn NyashBox>>>,
}
impl NyashBoxRegistry {
fn call_method(&self, type_id: u32, instance_id: u32, method_id: u32, args: &[u8])
-> Result<Vec<u8>, BidError> {
match type_id {
1 => self.call_stringbox_method(...), // 既存StringBox
6 => self.call_filebox_method(...), // プラグインFileBox
7 => self.call_futurebox_method(...), // 既存FutureBox活用
}
}
}
```
【設計原則】
1. **箱理論準拠**: すべてのBoxが同じ仕組みで扱える
2. **既存資産活用**: FutureBox等の既存実装を最大活用
3. **最低設計**: 動くものを最速で、複雑な機能は後回し
4. **二重実装回避**: 新しいBidFutureではなく既存FutureBox使用
5. **シングルスレッド中心**: Nyashの現実に合わせた設計
【具体的な利点】
- Nyashコード側は変更不要local file = new FileBox("test.txt")
- 既存StringBox, ArrayBox, FutureBoxと新しいプラグインFileBoxが同じ仕組み
- Handle("BoxType:id")による統一的なBox参照
- 非同期はFutureBoxで、新実装不要
【質問】
1. **箱理論の技術的妥当性**
- すべてをHandle統一する設計は適切か
- 既存BoxとプラグインBoxの統一的な扱いは可能か
- FFI境界でのBox参照としてHandle("Type:id")は適切か?
2. **最低設計のメリット・デメリット**
- プリミティブ + Handle のみの設計は十分か?
- 複雑な型Array, Map等を後回しにする判断は
- Phase 1で基本動作、Phase 2で拡張の戦略は妥当か
3. **既存資産活用の是非**
- 新しいBidFutureを作らずFutureBox活用は正解か
- 既存のStringBox等との統合アプローチは適切か
- 二重実装回避 vs 設計の純粋性のトレードオフは?
4. **実装現実性**
- 1週間でのPhase 1実装は現実的か
- 既存インタープリターとの統合難易度は?
- Linux x86-64限定での最初の実装は妥当か
5. **将来拡張性**
- この基盤で将来のgRPC/REST対応は可能か
- Transport抽象化への拡張パスは明確か
- P2PNyaMesh統合への道筋は適切か
「箱理論」という独特な哲学を持つNyashにとって、一般的なFFI設計とは異なる特殊なアプローチが必要だと考えています。この「最低設計から始めて段階的拡張」の戦略についての専門的見解をお願いします。

202
src/bid/bridge.rs Normal file
View File

@ -0,0 +1,202 @@
use crate::box_trait::NyashBox;
use super::{BidHandle, BidType, BidError};
use std::sync::Arc;
use std::collections::HashMap;
/// BID-FFI Bridge for Nyash Box types
/// Provides conversion between Nyash runtime values and BID handles
pub trait BidBridge {
/// Convert a Nyash Box to a BID handle
fn to_bid_handle(&self, registry: &mut BoxRegistry) -> Result<BidHandle, BidError>;
/// Get the BID type representation
fn bid_type(&self) -> BidType;
}
/// Registry for managing Box instances and their handles
pub struct BoxRegistry {
/// Maps handle to Arc<dyn NyashBox>
handle_to_box: HashMap<BidHandle, Arc<dyn NyashBox>>,
/// Next instance ID for each type
next_instance_id: HashMap<u32, u32>,
/// Reverse lookup: Arc pointer to handle
box_to_handle: HashMap<usize, BidHandle>,
}
impl BoxRegistry {
pub fn new() -> Self {
Self {
handle_to_box: HashMap::new(),
next_instance_id: HashMap::new(),
box_to_handle: HashMap::new(),
}
}
/// Register a Box and get its handle
pub fn register_box(&mut self, type_id: u32, boxed: Arc<dyn NyashBox>) -> BidHandle {
// Check if already registered by comparing Arc pointers
// We use the address of the Arc allocation itself as the key
let arc_addr = &*boxed as *const dyn NyashBox as *const () as usize;
if let Some(&handle) = self.box_to_handle.get(&arc_addr) {
return handle;
}
// Generate new instance ID
let instance_id = self.next_instance_id.entry(type_id).or_insert(1);
let handle = BidHandle::new(type_id, *instance_id);
*instance_id += 1;
// Register bidirectionally
self.handle_to_box.insert(handle, boxed.clone());
self.box_to_handle.insert(arc_addr, handle);
handle
}
/// Retrieve a Box by its handle
pub fn get_box(&self, handle: BidHandle) -> Option<Arc<dyn NyashBox>> {
self.handle_to_box.get(&handle).cloned()
}
/// Remove a Box from the registry
pub fn unregister(&mut self, handle: BidHandle) -> Option<Arc<dyn NyashBox>> {
if let Some(boxed) = self.handle_to_box.remove(&handle) {
let arc_addr = &*boxed as *const dyn NyashBox as *const () as usize;
self.box_to_handle.remove(&arc_addr);
Some(boxed)
} else {
None
}
}
}
/// Convert Nyash Box to BID handle
pub fn box_to_bid_handle(
arc_box: &Arc<dyn NyashBox>,
registry: &mut BoxRegistry,
) -> Result<(BidType, BidHandle), BidError> {
// Downcast to specific box types
if let Some(_string_box) = arc_box.as_any().downcast_ref::<crate::boxes::string_box::StringBox>() {
let handle = registry.register_box(
crate::bid::types::BoxTypeId::StringBox as u32,
arc_box.clone()
);
Ok((BidType::Handle { type_id: 1, instance_id: handle.instance_id }, handle))
} else if let Some(_integer_box) = arc_box.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() {
let handle = registry.register_box(
crate::bid::types::BoxTypeId::IntegerBox as u32,
arc_box.clone()
);
Ok((BidType::Handle { type_id: 2, instance_id: handle.instance_id }, handle))
} else {
Err(BidError::InvalidType)
}
}
/// Convert BID handle back to Nyash Box
pub fn bid_handle_to_box(
handle: BidHandle,
registry: &BoxRegistry,
) -> Result<Arc<dyn NyashBox>, BidError> {
registry.get_box(handle)
.ok_or(BidError::InvalidHandle)
}
/// Extract string value from a Box for TLV encoding
pub fn extract_string_value(arc_box: &Arc<dyn NyashBox>) -> Result<String, BidError> {
if let Some(string_box) = arc_box.as_any().downcast_ref::<crate::boxes::string_box::StringBox>() {
Ok(string_box.value.clone())
} else {
Err(BidError::InvalidType)
}
}
/// Extract integer value from a Box for TLV encoding
pub fn extract_integer_value(arc_box: &Arc<dyn NyashBox>) -> Result<i64, BidError> {
if let Some(integer_box) = arc_box.as_any().downcast_ref::<crate::boxes::integer_box::IntegerBox>() {
Ok(integer_box.value)
} else {
Err(BidError::InvalidType)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_box_registry() {
let mut registry = BoxRegistry::new();
// Create a mock box
let string_box = crate::boxes::string_box::StringBox::new("Hello");
let arc_box: Arc<dyn NyashBox> = Arc::new(string_box);
// Register it
let handle = registry.register_box(1, arc_box.clone());
assert_eq!(handle.type_id, 1);
assert_eq!(handle.instance_id, 1);
// Retrieve it
let retrieved = registry.get_box(handle).unwrap();
assert_eq!(Arc::as_ptr(&retrieved), Arc::as_ptr(&arc_box));
// Register same box again should return same handle
let handle2 = registry.register_box(1, arc_box.clone());
assert_eq!(handle, handle2);
}
#[test]
fn test_string_box_bid_conversion() {
let mut registry = BoxRegistry::new();
// Create StringBox
let string_box = crate::boxes::string_box::StringBox::new("Test String");
let arc_box: Arc<dyn NyashBox> = Arc::new(string_box);
// Convert to BID handle
let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap();
assert_eq!(handle.type_id, 1); // StringBox type ID
match bid_type {
BidType::Handle { type_id, .. } => assert_eq!(type_id, 1),
_ => panic!("Expected Handle type"),
}
// Extract string value
let value = extract_string_value(&arc_box).unwrap();
assert_eq!(value, "Test String");
// Round-trip test
let retrieved = bid_handle_to_box(handle, &registry).unwrap();
let retrieved_value = extract_string_value(&retrieved).unwrap();
assert_eq!(retrieved_value, "Test String");
}
#[test]
fn test_integer_box_bid_conversion() {
let mut registry = BoxRegistry::new();
// Create IntegerBox
let integer_box = crate::boxes::integer_box::IntegerBox::new(42);
let arc_box: Arc<dyn NyashBox> = Arc::new(integer_box);
// Convert to BID handle
let (bid_type, handle) = box_to_bid_handle(&arc_box, &mut registry).unwrap();
assert_eq!(handle.type_id, 2); // IntegerBox type ID
match bid_type {
BidType::Handle { type_id, .. } => assert_eq!(type_id, 2),
_ => panic!("Expected Handle type"),
}
// Extract integer value
let value = extract_integer_value(&arc_box).unwrap();
assert_eq!(value, 42);
// Round-trip test
let retrieved = bid_handle_to_box(handle, &registry).unwrap();
let retrieved_value = extract_integer_value(&retrieved).unwrap();
assert_eq!(retrieved_value, 42);
}
}

View File

@ -6,12 +6,14 @@ pub mod tlv;
pub mod error;
pub mod metadata;
pub mod plugin_api;
pub mod bridge;
pub use types::*;
pub use tlv::*;
pub use error::*;
pub use metadata::*;
pub use plugin_api::*;
pub use bridge::*;
/// BID-1 version constant
pub const BID_VERSION: u16 = 1;

View File

@ -38,6 +38,7 @@
*/
use crate::box_trait::{NyashBox, BoxCore, BoxBase};
use crate::bid::{BidBridge, BidHandle, BidType, BidError, BoxRegistry};
use std::any::Any;
use std::fmt::Display;
@ -116,4 +117,23 @@ impl Display for IntegerBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
impl BidBridge for IntegerBox {
fn to_bid_handle(&self, registry: &mut BoxRegistry) -> Result<BidHandle, BidError> {
use std::sync::Arc;
let arc_box: Arc<dyn NyashBox> = Arc::new(self.clone());
let handle = registry.register_box(
crate::bid::types::BoxTypeId::IntegerBox as u32,
arc_box
);
Ok(handle)
}
fn bid_type(&self) -> BidType {
BidType::Handle {
type_id: crate::bid::types::BoxTypeId::IntegerBox as u32,
instance_id: 0 // Will be filled by registry
}
}
}

View File

@ -28,6 +28,7 @@
* ```
*/
use crate::box_trait::{NyashBox, BoxCore, BoxBase};
use crate::bid::{BidBridge, BidHandle, BidType, BidError, BoxRegistry};
use std::any::Any;
use std::fmt::Display;
@ -184,4 +185,23 @@ impl Display for StringBox {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.fmt_box(f)
}
}
impl BidBridge for StringBox {
fn to_bid_handle(&self, registry: &mut BoxRegistry) -> Result<BidHandle, BidError> {
use std::sync::Arc;
let arc_box: Arc<dyn NyashBox> = Arc::new(self.clone());
let handle = registry.register_box(
crate::bid::types::BoxTypeId::StringBox as u32,
arc_box
);
Ok(handle)
}
fn bid_type(&self) -> BidType {
BidType::Handle {
type_id: crate::bid::types::BoxTypeId::StringBox as u32,
instance_id: 0 // Will be filled by registry
}
}
}