RPGにおける列挙型
ITは、数十年前に私がこの世界に入った頃から大きく変わりましたが、変わっていないこともいくつかあります。当時は、COBOLは終わったという記述をよく目にしたものですが、今も同じ記述を目にします。けれども、COBOLは60年を迎えて今なお健在です。当時、RPGは「Real Poor Garbage(どうしようもないがらくた)」の略だと酷評されていました。近頃は「レガシー」と言われているようですが、意味するところは同じであるようにも思われます。ところが、今のRPGは、前身となるビジネス プログラミング用言語のどれよりも優れているようです。
一般に、RPGには最新の言語に備わっている機能がないと思われているようです。とはいえRPGを使う必要があることを、私は理解しています。その1つの例が、列挙型のデータタイプです。
列挙型は、制限された許容値のセットを持つデータタイプです。列挙型の素晴らしい点は、変数に無効な値が割り当てられる可能性が排除されることです。JavaScript、C#、Pythonといった現在ホットな言語はそれらをサポートしていますが、新しいものというわけではありません。私は大学生時代にPascalで列挙型を使用していました。列挙型の詳細および用例については、 Wikipediaを参照してください。
RPGには、列挙型用の特別な構文はありませんが、既存の構文が十分に使えます。その使い方を示します。
何らかの一連のトランザクションを処理するサブプロシージャーを呼び出すプログラムがあるとします。このサブプロシージャーは、次の3つのモードのいずれかで動作できます。
- データを検証することができる。すなわち、エラーがないかデータをチェックする。
- データを検証して、検証をパスしたら、データをポストすることができる。
- 検証をスキップして、データをポストすることができる。
このようなサブプロシージャーは、おそらくサービス プログラムの一部であるのでしょうが、例を単純にするために、内部サブプロシージャーにします。
サブプロシージャーは、ステータス コードを返して、呼び出し元に結果を報告します。以下のステータス コードが返されます。
- サブプロシージャーはエラーなく実行された。
- サブプロシージャーは実行されたが、何らかの問題が想定されたため、1つ以上の警告メッセージを生成した。
- サブプロシージャーはエラー条件を見つけた。
以下は、これらのデータタイプが定義されるコピーブックです。
**free
dcl-s Action_t char(2) template;
dcl-c Validate 'V ';
dcl-c Post 'P ';
dcl-c ValidateAndPost 'VP';
dcl-s RunStatus_t packed(1) template;
dcl-c SuccessfulRun 0;
dcl-c RunWithWarning 1;
dcl-c RunFailed 2;
Action_tとRunStatus_tという2つのデータタイプを定義しました。データタイプ名の最後は「_t」にするようにしています。それぞれの宣言のTEMPLATEキーワードに注目してください。これにより、コンパイラーがメモリーを割り当てるのを防ぎます。
それぞれのデータタイプに対して、許容値のセットを定数として定義しました。RPGでは大文字小文字が区別されないため、定数が他のタイプの識別子と同じに見えるようにしたいと思います。
このコピーブックをインクルードするどのソース メンバーも、これらのデータタイプの変数および関数を定義することができます。
以下は、呼び出し元の例です。
**free
/include copybooks,DataTypes
dcl-s Stat like(RunStatus_t);
dcl-s BatchNumber packed(3);
Stat = ProcessBatch (BatchNumber: ValidateAndPost);
if Stat = RunFailed;
// do something to handle the error
endif;
dcl-proc ProcessBatch;
dcl-pi ProcessBatch like(RunStatus_t);
inBatch packed(3) const;
inAction like(Action_t) const;
end-pi;
if (inAction = Validate
or inAction = ValidateAndPost);
// calcs to validate the data
if . . . some error condition . . .
return RunFailed;
endif;
endif;
if (inAction = Post
or inAction = ValidateAndPost);
// calcs to post the data
if . . . some error condition . . .
return RunFailed;
endif;
endif;
return SuccessfulRun;
end-proc;
これらのデータタイプで定義されている変数、関数、および関数パラメーターに注目してください。
dcl-s Stat like(RunStatus_t);
dcl-pi ProcessBatch like(RunStatus_t);
inAction like(Action_t) const;
コンパイラーがリテラルの割り当てやリテラルの比較を妨げているわけではありませんが、私はそうしません。私はいつも定数を参照します。
return SuccessfulRun;
if Stat = RunFailed;
最後に強調しておきたい点は、これらのテンプレート変数は、ほとんどのケースで、本当に実際のデータタイプのように使用できるということです。たとえば、INZキーワードでこれらの定数を使用することができます。
dcl-s Stat like(RunStatus_t) inz(RunFailed);
これで絶対確実というわけでありません。コンパイラーは、変数に無効な値を割り当てたり、あるタイプのリテラルを別の適合するタイプの変数へ割り当てたりすることを防いでくれません。そのようなエラーを避けるのは訓練次第ですが、大変な作業というわけではありません。たいてい、そうしたエラーははっきりと目立っています。たとえば、これは適正といえます。
Color = Blue;
しかし、これはそうではありません。
Color = RunFailed;
新しい言語には、確かに、使用するそれなりの意味がありますが、だからと言って、新しいものは古いものよりも優れているはずだとして、すぐにRPGを捨て去ってしまうべきではありません。RPGは有用なビジネス プログラミング言語であり、信じようが信じまいが、巷でできないと言われている多くのことを実は行うことができるのです。