RPGのエキスパートであるジョン・パリスとスーザン・ガントナーが2019年秋のIBM iテクノロジー・リフレッシュで利用可能になったRPGの新機能について解説します。
2019/10/14 ジョン・パリス
毎年今頃、IBMはテクノロジー・リフレッシュ(TR)を出すのが通例です。普通そのTRでRPGの新機能が提供されます。今秋のTRも例外ではなく、RPGに3つの新機能がもたらされ、その内2つはとても重要なものです。
第1の機能はプロシージャ・オーバーローディングで、多くの人が長い間期待していたものです。
第2の機能は全く新しい「対の片方」の命令コードで、XML-INTO命令やDATA-INTO命令と対をなすものです。これはDATA-GEN命令と呼ばれ、RPGが通常のRPGデータをJSON文書、HTML、XML等々、基本的に任意の形式のデータに変換する機能を提供します。言い換えれば、XML-INTO命令やDATA-INTO命令の逆の働きをします。
第3の機能は上記の2つに比べずっと小さいものですが役に立つ機能です。それは、プロトタイプで指定されたパラメーターが、仕様に厳密に一致することを保証するための追加オプションです。
プロシージャ・オーバーローディング
オブジェクト指向プログラミングに精通しているなら、プロシージャ・オーバーローディングの概念は既にお馴染みの筈です。オブジェクト指向プログラミングの経験がない人には、例を使って説明するのが最善でしょう。
今、入力としてDate型の日付データを受け取り、曜日を表す1~7の数字を返す単純なサブプロシージャがあるとします。ここで、これと同じ機能を持つサブプロシージャを作るよう要求されたとしましょう。但し、この場合の入力は7桁のパック10進数形式の「日付データ」だとします。この要求に応える方法の1つは、問題のプログラマーに「馬鹿言うな」と言って、元々あるルーチンに対するパラメーターとして組み込み関数(BIF)の%DATEを使うことです。もう1つのもっと礼儀正しい方法は、別の名前とパラメーター・リストをもった新しいサブプロシージャを作り、代わりにそれを使うようにプログラマーに命じることです。もちろん、多分裏では新しいルーチンは単に日付の形式を変換し、元々のルーチンを呼び出すだけです。
誰かが会社のシステムには「日付」を含む8桁の文字フィールドもあると指摘するまでそれでよいことにします。そしてあなたの作ったルーチンがそれらの「日付」も処理できるのであれば本当に素晴らしいことです。しかし、そうすることで3つのルーチンが生み出され、すべてが異なる名前でありながら同じ基本的な機能を果たすことになります。それは紛らわしいだけでは済みません。
プロシージャ・オーバーローディングについて考えましょう。この機能により、単一の関数名を使用し、実際にどのルーチンを呼び出すかはコンパイラーに解決させることができます。お分かりのように、そのような機能はCONSTやVALUEのような既存のパラメーター・オプションを使ってできることを遥かに凌駕しています。
次の簡単な例を見てみましょう。
(A) Dcl-Pr DOW_Date int(5); inpDate date(*ISO) Const; End-Pr; (B) Dcl-Pr DOW_Num int(5); inpDate packed(7) Const; End-Pr; (C) Dcl-Pr DOW_Char int(5); inpDate char(8) Const; End-Pr; (D) Dcl-Pr DayOfWeek int(5) OVERLOAD( DOW_Date : DOW_Num : DOW_char); (E) dayNumber = dayOfWeek(aDate); dayNumber = dayOfWeek(numDate); dayNumber = dayOfWeek(charDate);
(A)、(B)、(C)はそれぞれ3つのプロシージャに対するプロトタイプです。そう、異なるデータ型のパラメーターそれぞれに1つのプロトタイプが依然として必要ですが、違いはプロシージャの起動にあります。3つの入力データ型のどれを扱うにしても、単にDayOfWeekを呼び出せばよいのです。呼び出し例を(E)に示します。
構造はとても簡単です。(D)のプロトタイプDayOfWeekは、それがオーバーロードするプロシージャの名前をOVERLOADキーワードのパラメーターとしてリストするだけです。また、指定されている戻り値(もしそれがあるのなら)は、リストされたプロシージャと同じでなければならないことに注意してください。OVERLOADを指定するプロトタイプはEND-PRを持つことができませんし、戻り値に関係のないパラメーターあるいは他のキーワードは指定できません。
コンパイラーはDayOfWeekの呼び出しに遭遇すると、関数に渡される実際のパラメーターを調べ、候補のプロシージャの内のどれを実際に使用するかを決定します。ですから上記の例では、パラメーターが数字であればDOW_Numが呼び出されます。もし、日付フィールドが渡されるなら、DOW_Dateが使われることにます。もう多分予想がつくと思いますが、文字フィールドを処理するにはDOW_Charが呼び出されます。
上記の単純な例では、単一パラメーターのデータ型とサイズによってどのルーチンを呼び出すかが決定されます。しかし、呼び出すルーチンの決定は、同様にパラメーターの数および各パラメーターのデータ型とサイズに基づいて行われる可能性もあります。
渡されるパラメーターに一致するプロシージャがリストされていない場合はどうなるのでしょう。その場合、コンパイラーは単にエラーメッセージを出すというのが答えです。しかし、渡されたパラメーターに一致する2つのプロシージャがあった場合はどうでしょう。このような場合、現時点ではコンパイラーは単にエラーメッセージを出します。私たちは将来いつかコンパイラーが「最もよく適合」するものを選択するアプローチを採るように変更されることを期待しています。しかし、今のところIBMは慎重すぎて失敗する方がましだと考えており、私たちがこの新しい機能に慣れるまでは多分これで良いのでしょう。
コードの重複を避けるため、DOW_CharとDOW_Numの両方を最終的にDOW_Dateを呼び出して実際の作業を行うようにコーディングしました。その意図を明らかにするためにDOW_Numに対するコードを以下に示します。DOW_Charのコードはこれにとてもよく似たものになるはずです。
この例では、すべての数値型の「日付」が*CMDY形式であると想定しています。もし実際にはそうでないなら、その日付が表す形式を指定する追加のパラメーターならびに変換を行うための追加ロジックが必要です。
(F)で入力パラメーターが実際に有効な*CMDY形式になっているかを検査しているのが分かると思います。もし日付の形式が無効ならば、曜日番号は-1に設定されます。しかし、もし日付の形式が有効なら、それは(H)で日付型に変換されます。その後、変換された日付は(I)で曜日を表す番号を計算するためにDOW_Dateに渡されます。最後に(J)で曜日を表す番号が呼び出し元に返されます。お気付きの通り、例えば%DateとDOW_Dateの呼び出しを1つにまとめることでステップ数を減らすこともできたのですが、何が行われているかを明確にしたかったので例示したようなコードにしました。
Dcl-Proc DOW_Num; Dcl-Pi DOW_Num int(5); inpDate packed(7) Const; End-Pi; Dcl-S workDate date(*ISO); Dcl-S dayNumber int(5); (F) Test(DE) *CMDY inpDate; If %Error; (G) dayNumber = -1; Else; (H) workDate = %Date( inpDate : *CDMY ); (I) dayNumber = DOW_Date( workDate ); EndIf; (J) return dayNumber; end-proc;
新しいプロトタイピング・オプション
パラメーター・プロトタイピングが追加されたのはRPGの大きな進歩でした。これにより、プログラムやプロシージャの呼び出し時にパラメーターの有効性検査ができるようになりました。しかし、1つの特定領域においてそれは常に欠点をもっています。それは、呼びだされるルーチンが8桁の文字列を期待しているのに6桁の文字列を渡してしまうという失敗をするのを防いでくれるのですが、要求された桁数よりも長いフィールド(例えば10桁の文字列)を渡してしまうのは防いでくれません。
大抵の場合、これは大したことではありませんが、たまに大きな問題になることがあります。例えば、呼び出されたルーチンが渡された値で単にフィールドの(この場合)8文字を埋めるような場合です。最後の2文字が何であれ手つかずのままになり、これが問題を引き起こす可能性があります。
OPTIONS(*EXACT) を指定する
このオプションを使うと、受容できるパラメーターに対する規則が強化されます。プロトタイプが8桁の文字列を要求しているところに10桁の文字列を渡そうとすると、コンパイラーはこれを拒絶します。
更に良いことに、このサポートはデータ構造(以下、DSという)にまで拡張されています。DSがLIKEDSを指定してパラメーターとして渡され、かつOPTION(*EXACT)が指定されていると、渡されるDSは同じDSにLIKEDSによって直接関連付けられていなければなりません。
以下のコードを見てください。私たちの言いたいことが分かるはずです。
dcl-s char8 char(8); dcl-s char6 char(6); dcl-ds Ds1 Qualified; A char(10); B char(20); End-Ds; dcl-ds Ds2 LikeDS(Ds1); dcl-ds Ds3 Qualified; A char(10); B char(20); End-Ds; Dcl-Pr Target1; parm1 char(6) Options(*Exact); End-Pr; Dcl-Pr Target2; parm1 LikeDS(Ds1) Options(*Exact); End-Pr; Target1(char6); // うまくいく例 Target1(char8); // これはエラーになる – パラメーターが長すぎる Target2(Ds1); // これは動作する Target2(Ds2); // これも動作する Target2(Ds3); // しかし、DSが同じ形でもこれはエラーになる
私たちはこの機能が好きです。長すぎるパラメーターが受け入れられるということが常に不安でしたが、この機能はこうした状況を改善してくれます。望むらくは、これがこの領域における他のいくつかの機能拡張の前兆であってほしいものです。例えば、データのコピーが常に作られるべきであることを指定し、それによって真の読み取り専用パラメーターを提供するためにCONSTというパラメーター・オプションを拡張してほしいと私たちは考えています。
現在、CONSTはそれがプロトタイプ・パラメーターのデータ型および/またはサイズと一致しなければならない場合にだけデータをコピーします。そのようなオプションにどのような名前が付けられるか確信はありません-多分*COPYでしょうか。他の人の考えも聞きたいものです。
DATA-GEN
最近RPGに関して最も要望の多い機能の1つは、IBMがXML-INTO命令の逆の機能をもつ命令を提供することでした。命令コードの名前としてXML-FROMという名前が度々提案されました。この発想の元は、XML-INTOがXML文書を分解してDSにデータを格納するのに対し、「XML-FROM」はこれと逆の操作を行う、つまりDS内のデータを取り込み、XML文書として形式を整えることにあります。XMLは多くの潜在的な出力形式の1つに過ぎないというだけでなく、そのような機能を提供することには多くの潜在的問題があります。JSONはどうですか?あるいはHTMLやCSVはどうでしょう?
IBMが「XML-FROM」を提供しないという決定をし、代わりにもっとずっと価値のある何か、すなわちDATA-GEN命令、を提供したのはそうした理由によります。DATA-GENはIBMがDATA-INTO命令で採ったのと同じ方式を採用しています。つまり、IBMはユーザーが自前の生成コードをプラグインできる命令コードのフレームワークを提供したのです。これにより、企業独自のデータ交換形式あるいはまだ発明されていない交換標準を含む望み通りの任意の文書タイプが生成できます。
IBMが提供している生成プログラムの例の中にはHTMLやXMLを生成する簡単な生成プログラムがあります。いつものことながら、そもそもこれらはデモ用のものであって実作業用のコードではありません。いずれもっと複雑な生成プログラムがサードパーティーから入手できるようになるでしょう。望むらくは、スコット・クレメントが、JSON用にDATA-INTOパーサーを追加したように、彼のYAJLツールキットにJSON生成プログラムを追加してほしいものです。
生成プログラムの作成作業は複雑なので、この新しい機能に関してそれにふさわしく且つそれが要求する詳細度で記事を書き上げるには2~3週間かかるでしょう。まもなく私たちは別の記事でこれを取り上げるつもりです。
RPGを前進させる
私たちはこれらの新しいオプションに興奮していますが、IBMがRPGを前進させ続けているという事実にもっと興奮しています。誰かがRPGは死にかけているなどと言っても信じてはいけません。それが正しくないことは新しいリリースごとに証明されているのですから。