DSL / Alternative Computational Models


DSL

代替計算モデル

一言要約

代替計算モデルは、宣言的プログラムの基盤を提供し、意図を理解しやすいプログラムとしてのDSLの土台となっている。ドメイン専門家が命令型でない考え方をとっているなら、代替計算モデルとDSLの組み合わせは非常に有効。

要約

  • DSLの利点は宣言的アプローチを後押しする点にあると言われている。ここでの「宣言的」は「命令的でないもの」を指す。
  • 主流のプログラミング言語は命令型計算モデルである。これは、定義された一連のステップで計算を定義する。
    • OO言語も土台は命令型
  • 命令型モデルに対する批判は数多くあるものの、依然として主流。これは、動作のわかりやすさに由来すると考えられる。
  • 理解のしやすさについて話す場合には、以下の2つがあることを認識すべし。
    • 意図のわかりやすさ
    • 実装のわかりやすさ

例を挙げる。

携帯電話を持っている	Y	Y	N	N
赤い車を持っている		Y	N	Y	N
ポイント			7	3	2	0

これを命令コードで表現すると、

   public static int CalcPoints(Application a) {
     if ( a.HasCellPhone &&  a.HasRedCar) return 7;
     if ( a.HasCellPhone && !a.HasRedCar) return 3;
     if (!a.HasCellPhone &&  a.HasRedCar) return 2;
     if (!a.HasCellPhone && !a.HasRedCar) return 0;
     throw new ArgumentException("unreachable");
   }

こうなるが、普通命令コードで同じことを書くと以下のようになる。

   public static int CalcPoints2(Application a) {
     if (a.HasCellPhone)
       return (a.HasRedCar) ? 7 : 3;
     else return (a.HasRedCar) ? 2 : 0;
   }

しかしこれではドメイン専門家の意図と離れてしまっているため、前者の書き方が望ましい。テーブルとコードは良く似ているが、順に実行されるif文、というテーブルには存在しない要素が使われており、これはノイズであると言える。この場合は大きな問題ではないが、これが問題になるような計算モデルもある。

代替案は、デシジョンテーブルの抽象を作ること。

     var table = new DecisionTable<Application, int>();
     table.AddCondition((application) => application.HasCellPhone);
     table.AddCondition((application) => application.HasRedCar);
     table.AddColumn( true,  true, 7);
     table.AddColumn( true, false, 3);
     table.AddColumn(false,  true, 2);
     table.AddColumn(false, false, 0);

これにより、以下のような利点が得られる。

  • 意図のより忠実な表現
    • 命令コードにあったような評価順序指定の排除
  • 構成誤りの検出が可能
  • 実行コンテキストの移動により、再コンパイルなしにルールを変更可能

こうした表現スタイルを「アクティブモデル」と呼ぶ。これは、OOの「アクティブオブジェクトモデル」に由来するが、オブジェクトモデルとは限らないためこのような名前とした。

アクティブモデルの特徴はその振る舞いがインスタンス同士の関係によって定義されていること。OOモデルの多くは振る舞いとデータを持つ、という意味でアクティブだが、必ずしもアクティブモデルであるとは限らない。

意味論モデルはしばしばアクティブモデルであり、代替計算モデルを提供する。

アクティブモデルの欠点は、コードを見ただけでは振る舞い全体がわからない点、さらに意図はわかりやすくなるが実装がわかりづらくなる点。このため、一般にはメンテナンスしづらいと評価されている。実際は、一旦理解できれば生産性は大きく向上するが、そこまで辿りつかない場合がほとんど。

DSLはアクティブモデルの理解しづらさをある程度和らげてくれる。

代替計算モデルはDSLを利用する理由のうち大きな部分を占める。ドメイン専門家が命令的ではない考え方をとっているなら、アクティブモデルとDSLの併用が有効。

代替モデルの例

デシジョンテーブルから。

プレミアム顧客	X	X	Y	Y	N	N
優先注文		Y	N	Y	N	Y	N
国際注文		Y	Y	N	N	N	N
手数料		$150	$100	$70	$50	$80	$60
アラート報告	Y	Y	Y	N	N	N

ドメイン専門家との対話で有効。

プロダクションルールシステムは、ルールに分割することで論理をモデル化する。ルールは以下のような形態を取る。

     if 
       mileage > 25000 
     then
       passenger.frequentFlier = true;
     if 
       passenger.frequentFlier
     then
       passenger.priorityHandling = true;

ルールとして条件とアクションは記述するが、ルールの実行やルール同士の結合はシステムが担う。上記の例では、最初のルールによって2番目のルールが実行(発火)されるかどうかが決まるため、ルール同士に関連がある。

あるルールの発火が他のルールの発火是非を変更する、という特性を「チェーン」と呼び、プロダクションルールシステムの重要な特徴である。これによってルールを独立して記述できるようになるが、暗黙的な振る舞いが増えるため問題も起こしがち。チェーンがない形態も存在する。

デシジョンテーブルはプロダクションルールシステムの一形態である、とみなすことも(論理的には)可能だが、論理の捉え方が異なっており、違うメンタルツールとみなすべき。

ステートマシンは、オブジェクトの振る舞いを状態の集まりへと分割し、振る舞いをイベントでトリガする形でモデル化する。例として、注文を表すステートマシンをUMLで表すと、以下のようになる。

order-processing-state.jpg

ステートマシンのコアな要素は状態、イベント、遷移。多くのバリエーションを持つが、差異の多くはアクションの起動方法にある。

一般にシステムは状態を持ち、状態に依存してイベントに対する振る舞いが変化するため、システムをモデル化する際によく使われる。

依存ネットワーク make、ant、rake等ビルドツールでおなじみ。test、compile、load data、generate codeという4つのタスクをモデル化した例が以下。

sketch.gif

generate codeタスクは2つのタスクから事前要求として設定されているが、実際には1度しか実行されない。

どの計算モデルをいつ用いるべきか、簡単に決まるようなガイドラインはない。妥当なモデルは問題をどう捉えたいのかによって決まる。まずは試してみること。この段階では、単純なDSLが役に立つ。DSLを読みやすく洗練させるのは、きちんとした意味論モデルが出来てからで良い。

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


担当者のつぶやき

OO言語の土台が命令型、というところには、FowlerのOO言語に対する見方がよく表れていると思います。これは賛否両論あると思いますが。

みんなの突っ込み