# 機能設計書 39-Proc拡張

## 概要

本ドキュメントは、mrubyのProcクラスにlambda?, source_location, inspect, parameters, ===, yield, to_proc, curry, <<, >>等の拡張メソッドと、Kernel#procメソッドを追加するgem（mruby-proc-ext）の機能設計を記述する。

### 本機能の処理概要

**業務上の目的・背景**：CRuby互換のProc操作メソッドを提供し、Procオブジェクトのイントロスペクション（lambda?, source_location, parameters）、関数合成（<<, >>）、カリー化（curry）、case文対応（===）等の高度な操作を可能にする。

**機能の利用シーン**：lambdaかprocかの判別（lambda?）、デバッグ時のソース位置確認（source_location, inspect）、パラメータ情報の取得（parameters）、case文でのProc利用（===）、関数合成によるパイプライン構築（<<, >>）、部分適用（curry）等。

**主要な処理内容**：
1. イントロスペクション: lambda?（lambda判定）、source_location（ソース位置取得）、inspect/to_s（文字列表現）、parameters（パラメータ情報）
2. 呼び出し: ===（call委譲、case文対応）、yield（call委譲）
3. 変換: to_proc（自身を返す）、curry（カリー化）
4. 合成: <<（右から左への合成）、>>（左から右への合成）
5. Kernel: proc（ブロックからProc生成）

**関連システム・外部連携**：なし。

**権限による制御**：なし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | 画面なし |

## 機能種別

関数オブジェクト操作 / メタプログラミング

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| args | Object | Yes(===, yield) | 呼び出し引数（可変長） | - |
| arity | Integer | No(curry) | カリー化のアリティ | デフォルトはself.arity |
| other | Proc | Yes(<<, >>) | 合成対象のProc | callに応答すること |

### 入力データソース

メソッド呼び出し時の引数。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Boolean | true/false | lambda?の判定結果 |
| Array/nil | [filename, line]/nil | source_locationの結果（C関数はnil） |
| String | String | inspect/to_sの文字列表現 |
| Array | Array of Array | parametersの結果（[[:req, :x], [:opt, :y], ...]形式） |
| Object | Object | ===, yieldの呼び出し結果 |
| Proc | Proc | to_proc, curry, <<, >>の結果 |

### 出力先

メソッドの戻り値。

## 処理フロー

### 処理シーケンス

```
1. lambda?: MRB_PROC_STRICT_Pマクロでproc構造体のstrictフラグを判定
2. source_location: irep→debug情報からファイル名と行番号を取得
3. inspect: "#<Proc:アドレス ファイル:行 (lambda)>" 形式の文字列を構築
4. parameters: OP_ENTER命令のaspecからパラメータ種別と名前を抽出
5. curry: 再帰的クロージャを生成し、引数が揃うまで部分適用を繰り返す
6. <<: ->(*args){call(other.call(*args))} で右から左への合成
7. >>: ->(*args){other.call(call(*args))} で左から右への合成
```

### フローチャート

```mermaid
flowchart TD
    A[curry呼び出し] --> B{lambda?}
    B -->|Yes| C[arityチェック]
    C --> D{self.arity != 指定arity?}
    D -->|Yes| E[ArgumentError]
    D -->|No| F[make_curry生成]
    B -->|No| F
    F --> G[再帰的クロージャ]
    G --> H{new_args.size >= arity?}
    H -->|Yes| I[pproc呼び出し]
    H -->|No| J[make_curry再帰]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | lambda?のSTRICTフラグ | MRB_PROC_STRICT_Pがtrueならlambda | lambda?呼び出し時 |
| BR-02 | C関数のsource_location | C関数（CFUNC）の場合はnilを返す | source_location呼び出し時 |
| BR-03 | aliasのsource_location | MRB_PROC_ALIAS_Pの場合はupperを辿る | source_location呼び出し時 |
| BR-04 | parametersのlambda/proc差異 | lambdaはreq/post、procはoptとして扱う | parameters呼び出し時 |
| BR-05 | C関数のparameters | C関数は空配列を返す | parameters呼び出し時 |
| BR-06 | curryのlambdaアリティ制約 | lambda時は指定arityがself.arityと一致する必要がある | curry呼び出し時 |
| BR-07 | curryの型保持 | lambdaのcurryはlambda、procのcurryはprocを返す | curry呼び出し時 |
| BR-08 | kernel_procのブロック必須 | ブロックなし呼び出しはエラー | proc呼び出し時 |

### 計算ロジック

- **parameters**: `OP_ENTER`命令の`aspec`ワードから`MRB_ASPEC_REQ/OPT/REST/POST/KDICT/BLOCK/KEY`各マクロでパラメータ数を抽出。`irep->lv`から名前シンボルを取得。
- **curry**: `abs = lambda{|a| a < 0 ? -a - 1 : a}`で負のarityを正に変換後、`make_curry`再帰クロージャで引数蓄積。
- **合成**: `<<`は`->(*args, **opts, &block){call(other.call(*args, **opts, &block))}`、`>>`は`->(*args, **opts, &block){other.call(call(*args, **opts, &block))}`。

## データベース操作仕様

該当なし。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | ArgumentError | curryでlambdaのアリティ不一致 | "wrong number of arguments (given X, expected Y)" |
| - | ArgumentError | proc呼び出し時にブロックなし | ブロック必須（&!指定） |

### リトライ仕様

なし。

## トランザクション仕様

なし。

## パフォーマンス要件

- lambda?: O(1)（フラグ参照のみ）
- source_location: O(1)（debug情報参照）
- inspect: O(1)（文字列構築）
- parameters: O(パラメータ数)（aspec解析 + lv走査）
- curry: O(1)（クロージャ生成）、呼び出し時はO(蓄積引数数)
- <<, >>: O(1)（合成Proc生成）

## セキュリティ考慮事項

なし。

## 備考

- CとRubyのハイブリッド実装
- lambda?, source_location, inspect, parameters, kernel_procがC実装
- ===, yield, to_proc, curry, <<, >>がRuby実装
- inspectはlambda時に" (lambda)"サフィックスを付加
- parametersはOP_ENTER命令がない場合やCFUNC時は空配列を返す
- curryはlambda/procの型を保持するために__send__(type)を使用

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: Proc構造体を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | proc.c | `mrbgems/mruby-proc-ext/src/proc.c` | MRB_PROC_STRICT_P, MRB_PROC_CFUNC_P, MRB_PROC_ALIAS_Pマクロ |

**読解のコツ**: `struct RProc`の`flags`フィールドにSTRICT（lambda）やCFUNC（C関数）などのフラグが格納されている。`body.irep`がRuby実装のIR表現、`body.func`がC関数ポインタ。

#### Step 2: C実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | proc.c | `mrbgems/mruby-proc-ext/src/proc.c` | 各メソッドのC実装 |

**主要処理フロー**:
- **41-46行目**: proc_lambda_p - MRB_PROC_STRICT_Pでlambda判定
- **49-69行目**: mrb_proc_source_location - CFUNC/ALIAS判定後、irep→debug情報から[file, line]取得
- **82-86行目**: proc_source_location - source_locationのエントリポイント
- **104-133行目**: proc_inspect - "#<Proc:addr file:line (lambda)>"形式の文字列構築
- **148-156行目**: kernel_proc - ブロック引数をそのまま返す
- **168-244行目**: mrb_proc_parameters - OP_ENTERのaspecからパラメータ情報を構築
- **247-253行目**: ROMメソッドテーブル（inspect, lambda?, parameters, source_location, to_s）
- **255-262行目**: gem_init - Kernel#procの定義

#### Step 3: Ruby実装を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | proc.rb | `mrbgems/mruby-proc-ext/mrblib/proc.rb` | Ruby実装メソッド群 |

**主要処理フロー**:
- **23-25行目**: === - call委譲（case文対応）
- **37-39行目**: yield - call委譲
- **51-53行目**: to_proc - selfを返す
- **71-96行目**: curry - 再帰的クロージャによるカリー化
- **110-112行目**: << - 右から左への関数合成
- **126-128行目**: >> - 左から右への関数合成

### プログラム呼び出し階層図

```
Proc#lambda?
    +-- proc_lambda_p (C実装)
            +-- MRB_PROC_STRICT_P(p)

Proc#source_location
    +-- proc_source_location (C実装)
            +-- mrb_proc_source_location
                    +-- [CFUNC] nil返却
                    +-- [ALIAS] p->upper辿り
                    +-- mrb_debug_get_position -> [filename, line]

Proc#parameters
    +-- mrb_proc_parameters (C実装)
            +-- [CFUNC] 空配列返却
            +-- PEEK_W(irep->iseq+1) -> aspec取得
            +-- MRB_ASPEC_REQ/OPT/REST/POST/KDICT/BLOCK/KEY
            +-- irep->lv -> 名前シンボル取得

Proc#curry
    +-- [Ruby実装]
            +-- abs (負のarity変換)
            +-- lambda? でアリティ検証
            +-- make_curry (再帰クロージャ)
                    +-- __send__(:lambda / :proc)
                    +-- pproc[*new_args] (引数充足時)

Proc#<< / Proc#>>
    +-- [Ruby実装]
            +-- -> (*args, **opts, &block) { ... }
```

### データフロー図

```
[入力]                       [処理]                          [出力]

Proc (self)     ──────▶ C/Ruby処理関数 ──────▶ Boolean/Array/String/Proc
args/other               イントロスペクション/合成   結果

[parameters詳細フロー]
irep->iseq (OP_ENTER)
    +-- aspec ──▶ req/opt/rest/post/kdict/block/key 数
    +-- irep->lv ──▶ パラメータ名シンボル
    ──▶ [[:req, :x], [:opt, :y], [:rest, :args], ...]
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| proc.c | `mrbgems/mruby-proc-ext/src/proc.c` | ソース(C) | C実装メソッド群（lambda?, source_location, inspect, parameters, kernel_proc） |
| proc.rb | `mrbgems/mruby-proc-ext/mrblib/proc.rb` | ソース(Ruby) | Ruby実装メソッド群（===, yield, to_proc, curry, <<, >>） |
| mrbgem.rake | `mrbgems/mruby-proc-ext/mrbgem.rake` | 設定 | gem定義 |
| proc.rb | `mrbgems/mruby-proc-ext/test/proc.rb` | テスト | テストケース |
