第1回目は、
DSL概要
DSL
今回の連載では、
DSLの必要性
なぜ、
Ruby on Railsの登場以降、
DSLは、
メタプログラミングとは、
ロジックを直接コーディングするのではなく、 あるパターンをもったロジックを生成する高位ロジックを定義する方法のこと。主に対象言語に埋め込まれたマクロ言語によって行われる。
wikipedia: メタプログラミングより引用
メタ
(meta-) とは、 「高次な―」 「超―」 「―間の」 「―を含んだ」 「―の後ろの」 等の意味の接頭語。ギリシャ語から。
wikipedia: メタより引用
メタプログラミングについて
実際に例を挙げてメタプログラミングの説明をします。
CSV
CSVファイルの仕様は、
attr_reader :id, :age
attr_writer :name
attr_accessor :color
(1)一般的なプログラム:
- a.メンバ変数に値を保持する方法
- 処理 :CSVの列名と一致するメンバ変数に、
取得したデータを設定する。 - 問題点:メンバ変数である、
「id, first_ name, last_ name, email」 をハードコーディングすることになるため、 列の増減、 列名の変更がある度にプログラムを修正する必要がある。汎用性に欠け、 ある特定のCSVファイルの読み込みしか出来ない。
- 処理 :CSVの列名と一致するメンバ変数に、
- b.ハッシュ
(連想配列) に値を保持する方法 - 処理 :CSVの列名をキー
(文字列型) にし、 値をペアにして値を保持する。 - 問題点:プログラムで自動的にキーを付け、
値をペアにして格納しているので、 一見良さそうですが、 値を取得するときにハッシュのキーが文字列であるため、 要素名として直接データにアクセスすることができない、 という問題がある。
- 処理 :CSVの列名をキー
(2)メタプログラミング:
一般的なプログラムでは実現することができなかった次の2つの点を実現することができます。
- a.プログラムの再利用
- b.データを自然な形で扱えるようにする
つまり、
まず、
しかし、
この例を作る際に参考にしたのが、

DSLのメリットとデメリット
DSLは万能ではありません。なぜなら、
DSLのメリット
- (1)
DRY (Don't Repeat Yourself) - 繰り返しの排除
(参考: 『達人プログラマ』 (Dave Thomas ISBN10:4894712741) - 似ている処理のコードの自動生成
(テンプレート)
- 繰り返しの排除
- (2)
生産性の向上 - プログラミングするコード量が少ない
- 可読性が高い
- (3)
ある特定の分野に従事している人 (特定言語のプログラマ) とコミュニケーションが可能 - XML、
CSS、 SQLなど
- XML、
DSLのデメリット
- (1)
設計が難しい - APIの設計が難しいように、
DSLの設計も難しい
- APIの設計が難しいように、
- (2)
読み難いコードになる可能性がある - たとえば、
Cのマクロ - 良く設計されていないDSL
- たとえば、
- (3)
マイグレーション - 下位互換の維持
- (4)
ハイリスク・ ハイリターン - 処理の結果が想定出来難くなる可能性が高い.しかし、
良く設計されたDSLでは、 破壊的な生産性を提供可能
- 処理の結果が想定出来難くなる可能性が高い.しかし、
身近にあるDSL
私達の身の回りには、
分類 | DSL |
---|---|
Java | ANT, Maven、 |
Ruby | Rails Validations、 |
その他 | SQL、 |
内部DSLと外部DSL
内部DSL
言語自身で強力なDSLを記述する事のできるLispは、
RubyはLispの血統であることから、
attr_reader :id, :age
attr_writer :name
attr_accessor :color
#内部DSLのソースコード
class Module
def attr_reader(*symbols)
symbols.each do |symbol|
class_eval %{
def #{symbol}
@#{symbol}
end
}
end
end
def attr_writer(*symbols)
symbols.each do |symbol|
class_eval %{
def #{symbol}= (value)
@#{symbol} = value
end
}
end
end
def attr_accessor(*symbols)
attr_reader(symbols)
attr_writer(symbols)
end
end
外部DSL
ホスト言語とは異なる言語
また、
rule "Due date for Test"
when
task : Task()
Test(id == Test.TEST)
then
setTestsDueTime(task, 10);
end
まとめ
DSLは、
次回は、