第1回目は、DSLの基本的な概念について説明します。
DSL概要
DSL(Domain Specific Language)は、ドメイン固有言語と訳されてます。DSLは、Java、C#などの汎用言語とは違い、ある特定の種類の問題に特化したコンピュータ言語です。
今回の連載では、DSLの世界を解き明かします。そして、実際にDSLをつくることで、よりDSLの理解を深め、「 用法・用量を守って使える」ようになるのが、今回のゴールです。
DSLの必要性
なぜ、DSLを学ぶ必要があるのでしょうか。
Ruby on Railsの登場以降、メタプログラミングという言葉を良く聞くようになりました。メタプログラミングというと、マクロ、コードの自動生成、テンプレートを上げることができます。
DSLは、メタプログラミングで使われます。メタプログラミングは、一般的なプログラムとはまったく違った方法をとっているので、その概念、仕組みを知る必要があります。
メタプログラミングとは、ロジックを直接コーディングするのではなく、あるパターンをもったロジックを生成する高位ロジックを定義する方法のこと。主に対象言語に埋め込まれたマクロ言語によって行われる。
wikipedia: メタプログラミングより引用
メタ(meta-)とは、「 高次な―」「 超―」「 ―間の」「 ―を含んだ」「 ―の後ろの」等の意味の接頭語。ギリシャ語から。
wikipedia: メタより引用
メタプログラミングについて
実際に例を挙げてメタプログラミングの説明をします。
CSV(Comma-Separated Values)形式のファイルを読み込み、プログラム内部にデータを保持するプログラムを作ることを考えてみます。
CSVファイルの仕様は、1行目に列名、2行目以降にデータが記述されてます。列はid、first_name、last_name、emailとします。
リスト 内部DSLの利用例(Ruby)
attr_reader :id, :age
attr_writer :name
attr_accessor :color
(1)一般的なプログラム:
a.メンバ変数に値を保持する方法
処理 : CSVの列名と一致するメンバ変数に、取得したデータを設定する。
問題点: メンバ変数である、「 id, first_name, last_name, email」 をハードコーディングすることになるため、列の増減、列名の変更がある度にプログラムを修正する必要がある。汎用性に欠け、ある特定のCSVファイルの読み込みしか出来ない。
b.ハッシュ(連想配列)に値を保持する方法
処理 : CSVの列名をキー(文字列型)にし、値をペアにして値を保持する。
問題点: プログラムで自動的にキーを付け、値をペアにして格納しているので、一見良さそうですが、値を取得するときにハッシュのキーが文字列であるため、要素名として直接データにアクセスすることができない、という問題がある。
(2)メタプログラミング:
一般的なプログラムでは実現することができなかった次の2つの点を実現することができます。
a.プログラムの再利用
b.データを自然な形で扱えるようにする
つまり、DSLを作ることで「プログラムを生成するプログラムを書く」ことが容易に解決できます。
まず、プログラムを生成する仕様を決めます。クラス名は、ファイル名から取得します。要素名(フィールド)は、1行目に記述されている列名から取得します。このプログラムを動的に生成します。動的に生成されたプログラムにアクセスすることで、CSVファイルから取得したデータにアクセスすることが可能になります。
しかし、このプログラムは、CSVファイルからデータを取得し、提供することのみを解決してくれます。これが、“ DSL(ドメイン固有言語)” と言われる所以です。この例のDSLは、一般的に内部DSLもしくは組み込みDSLと言われてます(後述「内部DSLと外部DSL」を参照) 。
この例を作る際に参考にしたのが、Ruby on RailsのActiveRecordです。
図1 一般的なプログラミングとメタプログラミングの違い
DSLのメリットとデメリット
DSLは万能ではありません。なぜなら、ある特定の問題の解決のみに提供されている言語だからです。次に、DSLのメリットとデメリットについて見ていきます。
DSLのメリット
(1)DRY(Don't Repeat Yourself)
繰り返しの排除(参考:『達人プログラマ』( Dave Thomas ISBN10:4894712741)
似ている処理のコードの自動生成(テンプレート)
(2)生産性の向上
(3)ある特定の分野に従事している人(特定言語のプログラマ)とコミュニケーションが可能
DSLのデメリット
(1)設計が難しい
(2)読み難いコードになる可能性がある
(3)マイグレーション
(4)ハイリスク・ハイリターン
処理の結果が想定出来難くなる可能性が高い.しかし、良く設計されたDSLでは、破壊的な生産性を提供可能
身近にあるDSL
私達の身の回りには、すでに多くのDSLが存在してます。しかしながら、まだDSLには明確な定義というのがありません。
表 身近にある代表的なDSL
分類 DSL
Java ANT, Maven、struts-config.xml、Seasar2 S2DAO、Hibernate Query Language、JMock expectations
Ruby Rails Validations、Rails ActiveRecord、Rake、RSpec、Capistrano、Cucumber
その他 SQL、CSS、Regular Expression(正規表現) 、Make、graphviz
内部DSLと外部DSL
内部DSL
言語自身で強力なDSLを記述する事のできるLispは、「 プログラム可能なプログラミング言語」と言われてます。この手法をとっているDSLは、内部DSLもしくは、組み込みDSLと言われてます。
RubyはLispの血統であることから、Ruby、Ruby on Railsを使って書かれたコードの中には、多くの内部DSLが含まれてい ます。以下のサンプルコードは、内部DSLを使って書かれてます。ここで重要なのは、Rubyのシンタックスを違反することなく書かれている、ということです。このことにより、ホスト言語のパワーと既存のツールのすべてを利用することができます。
リスト 内部DSLの例(Ruby)
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
ホスト言語とは異なる言語(XML、Makefileのような独自形式)で作成されたDSLは、外部DSLと呼ばれてます。UNIX伝統のリトル言語がそれに相当します。外部DSLの最大のメリットは、DSL設計者が自由にフォーマットを決められるということです。しかし、この方法を採用すると、フォーマットをパースする処理を書かねばなりません。
また、この手法を使ったDSLは、GUIツールを提供していることが多いのも大きな特徴です。Eclipseには、外部DSLをサポートする多くのプラグイン(Seasar2、Spring、Hibernate、JBoss Rules など)が既に存在してます。
外部DSLの例(JBoss Rules)
rule "Due date for Test"
when
task : Task()
Test(id == Test.TEST)
then
setTestsDueTime(task, 10);
end
まとめ
DSLは、多くのプログラマの身近な存在であることがわかりました。DSLを使っている、という認識がなかっただけで、それを意識することが新しいプログラミング技術を身につけることができると思っております。
次回は、今回取り上げたCSVファイルのデータロードのコードなどを示しながら、より詳細なDSLの世界をご案内いたします。