DSL / Schema-Definition Languages and Meta-Models


DSL

スキーマ定義言語とメタモデル

一言要約

要約

この本を通じて、セマンティックモデルを使うことの有用性を強調してきた。
ファウラーが見てきたすべての言語ワークベンチはセマンティックモデルを使用し、それを定義するツールを提供している。

言語ワークベンチのモデルと、この本でこれまでに使ったセマンティックモデルとの間には顕著な違いがある。
オブジェクト指向の偏屈者として、ファウラーはデータ構造とふるまいを組み合わせたオブジェクト指向のセマンティックモデルを自然と構築する。
しかしながら、言語ワークベンチはそのようには動作しない。
それらは典型的に特定の目的のDSL、スキーマ定義言語を用いて、モデルのスキーマ、すなわち、データ構造を定義する環境を提供する。
それらは通常コード生成を通じて、ふるまいのセマンティクスを別個の課題として残す。

この点で、「メタ」という語は絵の中に入り始め、そして物事がエッシャーの絵のように見え始める。
これはスキーマ定義言語がそれ自身がモデルであるセマンティックモデルを持つからである。
スキーマ定義言語のセマンティックモデルは、DSLのセマンティックモデルのメタモデルである。
しかし、スキーマ定義言語それ自身は、そのメタモデルがスキーマ定義言語であるそのメタモデルがスキーマ定義言語であるそのメタモデルが・・・であるセマンティックモデルを使用して定義されたスキーマを必要とする。

シークレットパネルの例の断片、特にactive状態からwaiting-for-light状態へ動くところについて、この断片の状態図を示す。

この断片は二つの状態とそれらを結ぶ遷移を示している。
Introduction()メソッドのなかで見せたセマンティックモデルで、セマンティックモデルのために定義したJavaクラスとフィールドを使ってこのモデルを状態クラスの二つのインスタンスと遷移クラスの一つのインスタンスとして解釈した。

class State {
  ...
}
class Event {
  ...
}
class Transition {
  State source, target;
  Event trigger;
  ...
}

Javaコードはスキーマを表す一つの方法で、もう一つの方法はクラス図を使用することである。

モデルのスキーマはモデルのコンテンツの中に持つことのできるものを定義する。
スキーマに遷移を加えない限り、状態図の遷移にガードを加えることができない。
これは、クラスとインスタンス、テーブルと行、レコード型とレコードといった、どんなデータ構造定義と比べても同様である。
スキーマはインスタンスに入るものを定義する。

この場合のスキーマはJavaのクラス定義であるが、Javaのクラスというよりオブジェクトの一束としてスキーマを持てる。
これは実行時にスキーマを操作するのを可能にする。
クラス、フィールド、オブジェクトに対する3つのJavaクラスを持つこのアプローチのありのままのバージョンを定義する。

class MClass...
private String name;
private Map<String, MField> fields;

class MField...
private String name;
private MClass target;

class MObject...
private String name;
private MClass mclass;
private Map<String, MObject> fields;

状態と遷移のスキーマを生成するための環境を使用することができる。

private MClass state, event, transition;
private void buildTwoStateSchema() {
  state = new MClass("State");
  event = new MClass("Event");
  transition = new MClass("Transition");
  transition.addField(new MField("source", state));
  transition.addField(new MField("target", state));
  transition.addField(new MField("trigger", event));
}

それから、単純な状態モデルを定義するスキーマを使用することができる。

private MObject active, waitingForDrawer, transitionInstance, lightOn;
private void buildTwoStateModel() {
  active = new MObject(state, "active");
  waitingForDrawer = new MObject(state, "waiting for drawer");
  lightOn = new MObject(event, "light on");
  transitionInstance = new MObject(transition);
  transitionInstance.set("source", active);
  transitionInstance.set("trigger", lightOn);
  transitionInstance.set("target", waitingForDrawer);
}

図に書かれたように、二つのモデルとしてこの構造を考えることは有用でありうる。
ベースモデルはグラント嬢のシークレットパネルの断片であり、MObjectsを含む。
二番目のモデルはMClassesとMFieldsを含み、また通常メタモデルとして言及される。
メタモデルはそのインスタンスが別のモデルのためのスキーマを定義する。

メタモデルはもう一つのセマンティックモデルにすぎないので、そのベースモデルに対してやるのと同様にポピュレートするためにDSLを容易に定義できるが、そのようなDSLをスキーマ定義言語と呼んでいる。
スキーマ定義言語はエンティティとエンティティ間の関連を定義する若干の定義方法をもつ、まさに単なるデータモデルの形式にすぎない
スキーマ定義言語とメタモデルには多くの相違点がある。

手でDSLを書くとき、メタモデルを作ることにおいてのポイントは通常ほとんどない。
たいていの状況で、構造的な定義を使用するホスト言語の能力を使うことは最良の策である。
例であるが、遷移元の状態を探したいなら、aTransaction.getSource()ではなくaTransaction.get("source")のように何かを呼ばなければならず、どのフィールドが有効なのかを探すのをとても困難にし、自身の方をチェックするのを強制させられるなどがある。

おそらくこの状況でメタモデルを使用しない最も大きな論拠はセマンティックモデルを適したオブジェクト指向のドメインモデルにする能力を失うということである。
メタモデルがセマンティックモデルの構造を定義する作業に(もしその場しのぎであっても)耐えられる一方、ふるまいを定義するのは本当にとても難しい。
データとふるまいを結びつける適したオブジェクトが欲しいのであれば、スキーマ定義のために言語自身のメカニズムを使用することでよりよくなれる。

良いツールを提供するために、ワークベンチは定義したスキーマを吟味して操作する必要がある。
この操作は通常メタモデルを使うときずっと楽になる。
それに加え、言語ワークベンチのツールがメタモデルを使用する多くの共通的な不利を克服する。
結果として、たいていの言語ワークベンチはメタモデルを使う。
ワークベンチはエディタの定義を促進させて、そしてモデルの中に存在できないふるまいに追加するのを手助けするのに使う。

メタモデルはもちろん、ただのモデルにすぎない。
他のいかなるモデルと同様、その構造を定義するスキーマを持つ。
例では、そのスキーマはMClass、MField、MObjectとして記述したものである。
これはそれでスキーマ定義システムそれ自身に機能するワークベンチ自身のモデリングツールを使えるようにし、DSLスクリプトを書くのに使用するのと同じツールを使ってメタモデルを生成できるようにする。
結果において、スキーマ定義言語はまさにそれ自身が言語ワークベンチの他のDSLである。

多くの言語ワークベンチがこのアプローチをとるが、ブートストラップワークベンチと呼んでいる。
一般に、ブートストラップワークベンチは、ツールがそれ自身を定義できるので、モデリングツールがあなた自身の作業に十分だろうという確信をより提供する。

しかし、エッシャーの絵画の内部のように自分自身を考え始めるポイントでもある。
モデルがメタモデルを使用して定義されたモデルにすぎないメタモデルを使用して定義されているならば、それはどこで終わる?
実際は、スキーマ定義ツールはいくつかの方法で特殊であり、そしてワークベンチにハードコードされた内容がいくつかある。
本質的に、スキーマ定義モデルについての特別なことはそれ自身を定義する能力である。

一般的な質問は何がスキーマ定義言語と文法の違いであるか、である。
短い答えは、文法はテキストの言語の具体的な構文を定義する一方、スキーマ定義言語はセマンティックモデルのスキーマの構造を定義する。
文法はそれで入力言語を記述するたくさんのもの含むであろう一方、スキーマ定義言語はセマンティックモデルをポピュレートするのにつかわれるどのDSLからも独立するだろう。 文法は解析木の構造を暗示し、ツリー構成規則とともにシンタックスツリーの構造を定義できる。 しかし、構文木はセマンティックモデルから通常は異なる。

図式を定義するとき、クラスやフィールドといったデータ構造に関して考えることができる。
実際、スキーマ定義の多くはセマンティックモデルの要素を保持できる論理的なデータ構造について考えることにかかわりがある。
しかし、スキーマに現れることができるさらなる要素、構造的な制約、がある。
DbCでの不変と等しい、セマンティックモデルの有効なインスタンスを作る制約がある。

構造的な制約はデータ構造定義のなかで表現されることができるものを超える、通常は妥当性検査ルールである。
データ構造はそれ自身が制約を暗示し、スキーマが保持できないセマンティックモデルに何も言えない。
上記の状態モデルは遷移のたった一つの対象状態があると伝え、それ以上入れるところがないのでさらに多くを追加できない。
データ構造によって定義されて強制される制約である。

たとえ属性を整数のフィールドで保持するとしても、人の数の足が0か1か2であるに違いないというような、データ構造に押し付けられる限界であるかもしれない。
制約は、多くのフィールドとオブジェクトを伴って、独断的に複雑であり得る。たとえば、人が彼自身の先祖であるはずがないと言うこと。

スキーマ定義言語はしばしば構造的な制約を表現するいくつかの方法をもたらす。
これは属性に範囲を適用するのを許可することと同じくらい制限されたものかもしれないし、あるいはどんな制約も表現することを許可する汎用言語であるかもしれない。
一つのいつもの限界は構造的な制約がセマンティックモデルを変えることはできず、単に問い合わせることができるだけということである。
このようにして、これらの制約はどんな鎖でつなぐこともない業務ルールシステムである。

ファウラーへのフィードバック

担当者のつぶやき

まとめ大変。。。


みんなの突っ込み