なぜ今さら「INTERNAL」なのか? ― 大規模改修で泣かないためのスコープ管理術
メインフレームの現場で数十年戦っていると、たまに「なぜこのプログラムはこんなにバグりやすいのか」という相談を受ける。その原因の多くは、実は複雑怪奇なロジックそのものではなく、「名前の衝突」と「不適切なスコープ設定」にあることが多いんだ。
PL/Iは非常に強力で柔軟な言語だが、その柔軟さが仇となり、意図しない変数の共有が起きることがある。特に、何万行もあるレガシーなソースコードを改修する際、「どこで宣言された変数が、どこまで影響を及ぼしているのか」を見誤ると、デバッグは地獄と化す。
今日は、PL/Iのスコープ管理の要である「INTERNAL属性」について、現場の知見を交えて深掘りしていく。
—
1. INTERNAL属性とスコープの基本原則
PL/Iにおける変数宣言のデフォルトは、原則としてそのブロック(BEGINやPROCEDURE)内でのみ有効な`INTERNAL`属性を持つ。これはつまり、他のブロックからは直接参照できないことを意味する。
しかし、大規模なバッチ処理で、複数の内部プロシージャ(INTERNAL PROCEDURE)を多用していると、同じ名前の変数が異なるスコープで定義され、本来の値を上書きしてしまう事態が発生する。これが「隠蔽(Shadowing)」だ。
なぜINTERNALを意識すべきか
1. 名前の衝突回避: 共通モジュールやCopybook(%INCLUDE)で定義された変数名と、ローカル変数のバッティングを防ぐ。
2. 保守性の向上: 「この変数はこのサブ処理の中でしか使わない」と明示することで、可読性が格段に上がる。
3. メモリ効率と安全性: 予期せぬ外部からの値の書き換えを物理的に遮断できる。
—
2. 実践コード:スコープを制御する現場の作法
以下のコード例を見てほしい。VSAMファイルからレコードを読み込み、内部処理で集計を行うようなバッチの雛形だ。
/i
/——————————————————————-/
/ PROGRAM-ID: BATCH001 /
/ 機能: VSAMレコード集計処理 /
/——————————————————————-/
BATCH001: PROCEDURE OPTIONS(MAIN);
/ 外部(グローバル)変数:全体で参照するフラグ等 /
DCL EOF_FLAG BIT(1) INIT(‘0’B);
/ 外部定義のVSAMレコード構造 /
%INCLUDE VSAM_REC;
/ メイン処理ループ /
DO WHILE(EOF_FLAG = ‘0’B);
CALL READ_VSAM_AND_CALC;
END;
/ 内部プロシージャ:スコープを限定した処理 /
READ_VSAM_AND_CALC: PROCEDURE;
/ INTERNAL属性:このプロシージャ内でのみ有効な一時変数 /
/ 外側の同名変数とは完全に別メモリとして扱われる /
DCL WORK_TOTAL FIXED BIN(31) INTERNAL INIT(0);
/ VSAM読み込み処理(ダミー) /
READ FILE(IN_FILE) INTO(VSAM_REC_AREA);
IF EOF THEN EOF_FLAG = ‘1’B;
/ 内部的な計算処理 /
WORK_TOTAL = VSAM_VAL + 100;
/ ONユニットでの制御(エラーハンドリングは局所化する) /
ON CONVERSION BEGIN;
PUT SKIP LIST(‘ERROR: 数値変換失敗’);
END;
/ 組み込み関数を活用した処理 /
IF VERIFY(VSAM_ID, ‘0123456789’) ^= 0 THEN DO;
/ ここで処理 /
END;
END READ_VSAM_AND_CALC;
END BATCH001;
—
3. トラブルを防ぐための「3つの鉄則」
現場で若手がよくやるミスと、それを防ぐためのアドバイスをまとめておく。
① `EXTERNAL` と `INTERNAL` を混同するな
モジュール間でデータを共有したいという安易な動機で`EXTERNAL`を多用してはいけない。これはグローバル変数と同じだ。後で「誰が値を変更したのか」を追跡するのに膨大な時間がかかる。基本は「必要なデータを引数として渡す(BYADDR / BYVAL)」こと。これがPL/Iで設計する際の鉄則だ。
② `ON` ユニットのスコープに注意せよ
`ON` ユニットは、定義されたブロック内だけでなく、その下位のブロックにも有効範囲が及ぶ。もし内部プロシージャ内で局所的なエラー制御をしたい場合は、必ずそのプロシージャの入り口で`ON`ユニットを定義し、終わる時に適切に`REVERT`(あるいは終了)させること。さもないと、バグの温床になる。
③ 名前空間を汚さない工夫
大規模なプログラムなら、変数名に接頭辞(例: `L_` = Local, `G_` = Global)を付けるのが泥臭いが最も確実だ。ツールで解析する際も、このプレフィックスがあるだけで追跡の効率が倍以上変わる。
—
最後に:システムアーキテクトからの助言
「PL/Iは古い」と口にするエンジニアもいる。だが、数十年稼働し続ける基幹システムにおいて、この言語が持つ「メモリ管理の厳密さ」と「構造化の柔軟性」は、現代の言語にも決して引けを取らない。
INTERNAL属性を適切に使いこなすことは、単なるコーディングの作法ではない。それは、「システムにカプセル化という秩序を持ち込むこと」に他ならない。
次にバッチの改修を行う際は、ぜひ自分のコードが「どこまで見えているのか」を意識してほしい。それができるようになった時、君はもう一段階上のメインフレーム・アーキテクトに近づいているはずだ。
何か具体的なデバッグで行き詰まったら、またいつでも相談してくれ。現場の知恵で解決しようじゃないか。
