2020年3月31日火曜日

Kaleidox: モデル駆動開発

Kaleidoxはモデル駆動開発のアクション言語を指向しています。実用言語の観点から、データ操作の枠組みとしてRecordとTableを中心とした機能群を用意しています。

一方、モデル駆動開発に適用するためにはモデル側との連携も必要になります。

今回はモデル駆動開発のためのモデル定義とアクション言語の連携について、現時点で実装している簡単な機能を例に探っていきます。

初期設定

以下のinit.kldを用意します。

* env

db.default.driver="org.h2.Driver"
db.default.url="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1"

* voucher

** City

#+caption: 特性一覧
| 特性 | 名前                | 型     | 多重度 | ラベル       |
|------+---------------------+--------+--------+--------------|
| 属性 | name                | int    |      1 | 都市 |
| 属性 | latitude            | string |      1 | 緯度         |
| 属性 | longitude           | int    |      1 | 経度         |
| 属性 | average_temperature | int    |      1 | 平均気温     |
| 属性 | precipitation       | int    |      1 | 降水量       |

* prologue

store-create 'City 'city

この初期設定により以下の設定が行われます。

  • メモリDBの初期化
  • モデルの定義
  • モデルに従ったデータベースのテーブルの作成
メモリDBの初期化

envセクションで組み込みRDBのH2をメモリモードで起動するように記述しています。

モデルの定義

voucherセクションでモデルの定義を行っています。

モデルに従ったデータベースのテーブルの作成

prologueセクションでテーブルを作成する記述を行っています。

モデル

voucherセクションで以下のモデルを記述しています。

ボクは2008年ごろから帳票指向アーキテクチャをターゲットにしたモデリングの手法SimpleModelingを整備しています。

SimpleModelingではSimpleModelerというモデルコンパイラでプログラムの自動生成を行うことが可能です。

voucherはSimpleModelingのアーキタイプで「伝票」を記述するためのモデルです。一般のOOADではValue ObjectやDTO(Data Transfer Object)という方向性のモデルです。

SimpleModelingでは、voucher以外にも多数の種類のモデルを定義していますが、現時点ではKaleidoxではvoucherのみサポートしています。

現在SimpleModelerの新版を開発中で、これをKaleidoxに組み込めば各種モデルの操作をKaleidoxで行うことができるようになる予定ですが、現在は暫定的にvoucherのみ扱い可能にしています。

voucherセクションで定義しているvoucherは都市の緯度・経度、平均気温、降水量を定義した以下のものです。

* voucher

** City

#+caption: 特性一覧
| 特性 | 名前                | 型     | 多重度 | ラベル       |
|------+---------------------+--------+--------+--------------|
| 属性 | name                | int    |      1 | 都市 |
| 属性 | latitude            | string |      1 | 緯度         |
| 属性 | longitude           | int    |      1 | 経度         |
| 属性 | average_temperature | int    |      1 | 平均気温     |
| 属性 | precipitation       | int    |      1 | 降水量       |

この表の定義によってvoucher Cityが定義されます。

テーブルの作成

前述のinit.kldのprologueセクションは以下になっています。

* prologue

store-create 'City

store-create関数はテーブルの作成を行います。第1引数にモデル名Cityを指定しています。

このためKaleidox起動時にprologueセクションでこのstore-create関数が実行され、バウチャーCityのモデル定義に従ったテーブルCityがメモリDB内に作成されます。

今回はテスト目的でメモリDBを使用していますが、MySQLなどのRDBを使う場合にすでにテーブルが作成されている場合はこの処理は不要です。

データ格納

init.kldの設定によりKaleidox起動後はテーブルcityが作成されています。このテーブルにデータの格納を行ってみます。

都市,緯度,経度,平均気温,降水量
札幌,43.055248,141.345505,8.0,1158
仙台,38.254162,140.891403,11.9,1219
東京,35.680909,139.767372,15.3,1460
名古屋,35.154919,136.920593,14.9,1575
大阪,34.702509,135.496505,16.2,1400
広島,34.377560,132.444794,15.0,1603
福岡,33.579788,130.402405,16.0,1690
那覇,26.204830,127.692398,22.4,2128

「file:city.csv」によりcity.csvを読み込みます。 city.csvはTableオブジェクトとして読み込まれます。

kaleidox> file:city.csv
Table[5x8]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━┓
┃都市  │緯度     │経度      │平均気温│降水量┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━┿━━━━━━┫
┃札幌  │43.055248│141.345505│8.0     │1158  ┃
┃仙台  │38.254162│140.891403│11.9    │1219  ┃
┃東京  │35.680909│139.767372│15.3    │1460  ┃
┃名古屋│35.154919│136.920593│14.9    │1575  ┃
┃大阪  │34.702509│135.496505│16.2    │1400  ┃
┃広島  │34.377560│132.444794│15.0    │1603  ┃
┃福岡  │33.579788│130.402405│16.0    │1690  ┃
┃那覇  │26.204830│127.692398│22.4    │2128  ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━┛

読み込んだTableオブジェクトをテーブルCityに書き込みます。

Tableのヘッダにあるカラム名をキーにしてテーブルに書き込みます。このカラム名はVoucherモデルの名前だけでなくラベルとも照合されます。

たとえばVoucherモデルの定義ではCityオブジェクトの都市名を格納する属性はnameですが、ラベルが「都市」になっています。このためTableオブジェクトのカラム名nameだけでなくカラム名「都市」も格納対象となります。他のカラムも同様に、Cityオブジェクトの各属性とも対応付けられます。そして、Cityオブジェクトを格納するCityテーブルの該当するカラムとの対応付けも行われます。

テーブルへの追加格納はstore-insert関数で行います。

第1引数にテーブル名、第2引数にTableオブジェクトを指定します。ここではTableオブジェクトの読み込み直後で、Tableオブジェクトがスタックの先頭に積まれているので第2引数を省略してスタック上のTableオブジェクトを指定しています。

kaleidox> store-insert 'City
(0 0 0 0 0 0 0 0)

store-insert関数の返却値は新規作成したレコードのIDのリストです。CityテーブルはIDを持たないテーブルのためレコード数分の0を要素として持つリストが返されています。

データの読み込み

store-select関数でテーブルに格納したデータを読み込みます。

kaleidox> store-select 'City
Table[5x10]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━┓
┃name  │latitude │longitude │average_temperature│precipitation┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━━━━━━━━━━━━┿━━━━━━━━━━━━━┫
┃札幌  │43.055248│141.345505│8.0                │1158.0       ┃
┃仙台  │38.254162│140.891403│11.9               │1219.0       ┃
┃東京  │35.680909│139.767372│15.3               │1460.0       ┃
┃名古屋│35.154919│136.920593│14.9               │1575.0       ┃
┃大阪  │34.702509│135.496505│16.2               │1400.0       ┃
┃広島  │34.37756 │132.444794│15.0               │1603.0       ┃
┃福岡  │33.579788│130.402405│16.0               │1690.0       ┃
┃那覇  │26.20483 │127.692398│22.4               │2128.0       ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━━━━━━━━━━━━┷━━━━━━━━━━━━━┛

store-select関数のデフォルト動作ではモデルの属性名が使用されます。このため、都市はname、緯度はlatitudeといったヘッダ名を使用したTableオブジェクトとなっています。

プログラム的にデータを処理する場合はこの方が都合がよいですが、外部出力を行う場合はモデル側のラベル名がTableオブジェクトのヘッダ名になっている方が便利です。

このための指定を行うオプションとしてstore-select関数ではtable-headerスイッチを用意しています。

以下では(Lispの)キーワード指定によってtable-headerスイッチにlabelを指定しています。このためstore-select関数では、データを読み込むTableオブジェクトのカラム名をモデルのラベル名にしています。

kaleidox> store-select :table-header 'label 'City
Table[5x10]
kaleidox> :show:print
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━┯━━━━━━┓
┃都市  │緯度     │経度      │平均気温│降水量┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━━━┿━━━━━━━━┿━━━━━━┫
┃札幌  │43.055248│141.345505│8.0     │1158.0┃
┃仙台  │38.254162│140.891403│11.9    │1219.0┃
┃東京  │35.680909│139.767372│15.3    │1460.0┃
┃名古屋│35.154919│136.920593│14.9    │1575.0┃
┃大阪  │34.702509│135.496505│16.2    │1400.0┃
┃広島  │34.37756 │132.444794│15.0    │1603.0┃
┃福岡  │33.579788│130.402405│16.0    │1690.0┃
┃那覇  │26.20483 │127.692398│22.4    │2128.0┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━━━┷━━━━━━━━┷━━━━━━┛

チャート表示

最後に、テーブルから読み込んでみたデータをチャートに表示しましょう。ここからは前回「Kaleidox/単回帰分析」と同じ処理です。詳細はそちらを参照してください。

まずTableオブジェクトからチャートに表示するデータを抽出します。

kaleidox> table-select 0,1,3 x
Table[3x8]
kaleidox> :show:print
Table[3x8]
┏━━━━━━┯━━━━━━━━━┯━━━━━━━━┓
┃都市  │緯度     │平均気温┃
┣━━━━━━┿━━━━━━━━━┿━━━━━━━━┫
┃札幌  │43.055248│8.0     ┃
┃仙台  │38.254162│11.9    ┃
┃東京  │35.680909│15.3    ┃
┃名古屋│35.154919│14.9    ┃
┃大阪  │34.702509│16.2    ┃
┃広島  │34.377560│15.0    ┃
┃福岡  │33.579788│16.0    ┃
┃那覇  │26.204830│22.4    ┃
┗━━━━━━┷━━━━━━━━━┷━━━━━━━━┛

このTableオブジェクトをtable-chart関数でチャートに表示します。

まず、抽出後のTableオブジェクトを変数xに束縛します。Tableオブジェクトは抽出直後なのでスタックの先頭に積まれています。setq関数の第2引数を省略することでスタック上のTableオブジェクトが束縛対象となります。

kaleidox> setq x
kaleidox> table-chart x

以下のようなチャートが表示されます。


また単回帰分析を行う場合はanalyzeスイッチにsimple-regressionを指定します。

kaleidox> table-chart :analyze 'simple-regression x

以下のようなチャートが表示されます。


まとめ

頭出しレベルですが、モデル駆動開発のイメージを確認するために簡単な連携例を説明しました。

基本的な方向性としては、モデル定義を行った後は簡単なアクション言語による記述のみでアプリケーションを記述したいということです。

モデル定義がのままプログラミングにつながることで、モデルをプログラムに翻訳することによって起きているプログラミングの工数を大幅に低減することできます。また、これと同じぐらい重要なのはモデルとプログラムのインピーダンスミスマッチをなくすることができることです。モデルとプログラミングが一体となることで、よりモデリングに集中することができます。

SimpleModelerの改版版は現在開発中ですが、これをKaleidoxに組み込むことでより広範囲のモデルをモデル駆動の対象としてアプリケーション開発に活用できることができるようになります。

諸元

  • Kaleidox : 0.1.10
気象データ

以下のページ「5.おもな都市の月平均気温・月降水量」のデータを使用しました。

http://www.biodic.go.jp/reports/2-2/hyo/aa126_001.html