たなかこういちの開発ノート

システム開発に携わる筆者が、あれこれアウトプットするブログ

DSLは“超高級言語”(DSLなどについて整理するメモ、その2)

以前の記事で、DSL(Domain Specific Language)の仕組みの在り方のバリエーションを整理してみた。今回は、DSLの成立過程についての私の見立てを記す。なお、本記事中で単に「DSL」と云った場合、「外部DSL」を意味する。
 
 
プログラミング言語高級化の流れと、その先端に位置付くDSL
 
アセンブラは低級言語で、Cは高級言語(High-Level Language)、Javaはさらに高級な言語、という言い方がある/あった。DSLはこの考え方の延長に置かれるものと捉える。つまり、現時点で考え得る「最高級」、もしくは「“超”高級」の言語、という訳だ。
 
より「高級」であるとは、目的の“ドメイン”のセマンティックスをより直接的に記述できる言語要素を持つ、ということである。アセンブラで、「レジスタをPushしてJump」と記述していたのは、「関数(サブルーチン)」を表したかったからで、構造化プログラミングが導入されたC言語では、そのまま「関数」と記述することができる。C言語で「特定のデータ構造とそれを操作する関数群のセットを、一つのモジュールで管理」していたのは、「オブジェクト」を表したかったからで、オブジェクト指向が導入されたJava言語では、そのまま「クラス」と記述することができる。
 
「高級化」とは、名前の付けられたパターンの認識とその積み上げ
 
しかしこの発展は、「プログラミング上の定石=パターンに名前を付けた」、「名前の付いたパターンがあるならば、そのパターンを表す言語要素を導入してしまえば、毎度毎度のボイラープレートもしくはグルーコードを略せる」という話である。ことの本質は、用いるプログラミング言語に特定の言語要素が装備されているか否かということではなく、プログラミング上の特定のパターンを認識/理解しているか、というところにある。パターンを認識、理解してさえいれば、言語にその機能がなくても、そのパターンに依るところのプログラミングパラダイムの実践が可能である。(※C言語でオブジェクト指向を実践していた事例は沢山あって、例えばWin32 APIはC言語ベースだが、オブジェクト指向設計されたものだ。)
 
もうひとつ言及しておくべきことがある。プログラミング言語高級化の流れにおいて、新しい言語は古い言語のセマンティックスあるいはパラダイムを否定しているわけではなく、内包しているという点である。今まで積み上がってきたパターンは当然全て有用なのである。それの上積みとして新たなパターンを追加・拡張している。オブジェクト指向は構造化プログラミングの拡張と云える。(※「オブジェクト指向」の学術的起源については本来こうだという議論はあるだろうが、産業界で現に用いられているプログラミング言語で現に導入されているオブジェクト指向については、「構造化プログラミングの拡張として導入された」と位置付けて捉えられる。)「レジスタをPushしてJump」してることなどもう誰も意識しないが、それでも、英数字に続いて丸括弧を書く度に「レジスタをPushしてJump」しているのだ。
 
とは言えとは言え、人間の認識能力の限界もある。「関数」と「構造体」があったから「オブジェクト」というパターンを想起できたのであって、「レジスタをPushしてJump」、「グループ化した定数群で変位指定しつつアドレッシング」という世界から、一足飛びに「オブジェクト」というパターンを想起するのはなかなか難しいだろう。「レジスタをPushしてJump」が「関数」として、「グループ化した定数群で変位指定しつつアドレッシング」が「構造体」としてパターン認知されるという段階は、やはり経る必要があっただろう。
 
DSLは、ターゲットとなる“ドメイン”を限定することで、さらなる高級化を実現する
 
DSLは、以上のようなプログラミング言語高級化の流れの先端に位置付くと考える。
 
業務システムには、「永続化データのCRUD、参照はSQL風問い合わせ、更新時にはバリデーション」といった“パターン”が見出される。このパターンに例えば「エンティティ」とか「集約」とかの名前を付けている。業務システム向けDSLは、こういった「エンティティ」あるいは「集約」を、まさに言語要素として装備している訳である。
 
ただし、「DSL」という高級度に至って、前段で「業務システム向けDSL」と書いたように、Java言語までとは異なる様子が発生している。それはそのDSLを適用できるドメインが限定されている、ということである。Java言語までは汎用言語だった。(※ここで汎用とは、おおよそ「言語がチューリング完全であり、かつ、実行環境または実行時ライブラリに任意のコンピューティング資源へアクセスできるような機能拡張性が装備されている」ことを意味する。)
 
例えば、「プリンター制御システム」、「ゲーム(※「ぷよぷよ」を想定しよう)」、「生産管理」、「管理会計」という4つのドメインで考える。アセンブラ、C、Javaまでは、この4つのドメイン全てを実装できる。昨今の自動生成系・高速開発系ツールは、概ね業務システム一般をターゲットとしている。つまり「生産管理」と「管理会計」は実装できて、「プリンターの制御系」と「ぷよぷよ」を実装することはできない。ある種の会計パッケージ製品があって、それのカスタマイズ情報・構成情報一式をXMLなどでエクスポートできるとする。そのXMLは、明らかに当該会計パッケージが対象とするドメインのDSLである。そのDSLは「管理会計」を実装可能で、「プリンターの制御系」、「ぷよぷよ」、そして「生産管理」を実装できない。その代わり、自動生成系・高速開発系ツールや業務パッケージは、それぞれのターゲットのドメインにおいて、Java等の汎用言語とは全くレベルの違う“高級度”を達成しているのだ。
 
一般にドメインを限定すればするほど、言語の「高級度」は上げられるものである。
 

 
横軸Dは「ドメインの広さ」(※右が広い)を表し、縦軸Lは「言語の文法の高級度あるいは抽象度」(※上が高級度/抽象度が高い)を表す。横軸の最も右端は「全ての情報システム」というドメインを対象にするとしている。そこでは、高級度/抽象度はそれほど高くはなく、汎用言語の範疇に収まっている。対して、横軸の最も左端は「(なんらかの)単一タスク」がドメインだとしている。例えばMavenによる「Javaプログラムのモジュール構成定義」などを想定する。ここまでドメインを絞れば、その“言語”の文法は最も高級度/抽象度の高いものを用意できる。このような“言語”は、言語というよりも単に「設定ファイル」と称されることが多いだろう。そして、自動生成系・高速開発系ツールや特定の業務パッケージは、中央の領域あたりを狙ったものと云える。
 
マクロ≒メタプログラミングが多用され始めたら次世代高級化言語登場の機運?
 
アセンブラからC言語に至る途中に、「マクロ・アセンブラ」なるものが登場した。アセンブラコードの定型パターンをマクロでイディオム化できる訳だ。これにより「関数(サブルーチン)」や「構造体」が作成された。今のJavaはアノーテーション・プロセッシングしまくりである。Java言語のアノーテーションは、要はJava言語にとってのマクロ機能だ。ScalaのハイエンドでもScalaのマクロによる魔術世界が広がっている様である(※あまり知らない)。私の知っている事例はこの三つしかないが、ある時代で用いられている言語のマクロ、つまりメタプログラミング機能が多用され始めたら、そのマクロ実装が狙っているところの次の段階の高級度の新言語の受け入れ余地が高まっている事の現れなのではないか、という仮説をここに印しておく。昨今、高速開発系ツールの受け入れ余地も高まっているという。Java言語でのプログラミングがアノーテーションだらけに進化(?)していることと、高速開発系ツールが受け入れられつつあることは、何か同じ要因に基づいているのではないだろうか。
 
※以前にこのような記事を書いたが、その記事の仮説と絡めると、当該次世代超高級言語の普及には、言語に対応した何らかのプラットフォームも同時に想定せねばならない。加えて同記事では次世代は「関数型言語/リアクティブ・プラットフォーム」だとしている。本記事の云う超高級言語とその普及は、どのように位置付けられると見通せるか、関数型との絡みを紐解きつつの追加の考察がしばし必要そうだ。
 
 
<追記>
というわけで関数型について今思うことに触れておく。産業界で現に用いられているプログラミング言語に関数型が導入されつつあるのは、この記事で云うところの高級化の流れとは直接は関係ないと考える。その一方、関数型は、(任意の)(内部)DSLのホスト言語として有用な特性を既存言語に提供し、DSL化を促進する役目を(結果的に)果たしていると考える。
 
関数型導入前の、産業界で用いられているプログラミング言語のメインストリームは、万能チューリングマシンを起源にしている手続き型である。その点C++やJavaも万能チューリングマシンの子孫だ。関数型は、万能チューリングマシンと異なるラムダ計算機を起源にする。この2つは、(産業界に現に導入された形態としては、)根本的に思考/指向が異なっていると理解している。手続き型はボトムアップ的思考が可能で関数型はトップダウン的思考が強制される。データ処理を調理に例えるなら、手続き型は目の前の素材の扱いから始めることができて、関数型は料理の完成イメージから始めなければならない。手続き型は「コントロール・フロー」を記述するが、関数型は「データ・フロー」を記述する。
 
万能チューリングマシンの末裔はオブジェクト指向に至って、汎用言語のままでの進化余地が無くなって来た様にも思える。Java7時点で実際しばし停滞もあり、Javaは終わったとも言われた。次の高級度に進むためにドメイン限定のDSLへ移行(※上方向へのシフト)していくか、と思われたところ、関数型の導入が始まった。関数型は汎用言語のままで発展可能な“新大陸”をもたらした。(※横方向へのシフト)
 
関数型の導入自体は、直接は言語高級化の流れとは関係ないと考えるのだが、関数型は、アルゴリズムをライブラリー化できるという、そのセマンティックス上の特徴から、結果的に内部DSLのホスト言語として有用な特性を提供した。処理系に任意の新しい定義済み振る舞いを導入できるので、任意の新しい実際に動くセマンティックス体系を導入できる、こととなる。中上級のScalaプログラミングは、ほぼほぼ個別要件ローカルなミニDSLを作っていくような作業となっている様である。(※よく知らない)
 
・・・いや、ちょっと言い直してみると、言語高級化を模索する中での関数型導入は、意図的とは言えずとも元より蓋然性が高い話だったのかもしれない。以前考察したように、OOは静的構造のモデル化に有用だったが動的振る舞いにはアプローチできていなかった。動的振る舞いのモデル化に何とかアプローチしようとした試みの過程で関数型の有用性が見出されたのだ、と云える。そうしたら、結果として、静的構造、動的振る舞いの両側面のモデル化のための道具がそろうこととなり、汎用の、内部DSL“ホスト言語”もしくは外部DSL“ネイティブ言語”、としてのお膳立てがそろった、のだ。
 
なお、関数型ではトップダウン指向/思考やデータ・フロー記述が強制されるが、手続き型でもトップダウン指向/思考やデータ・フロー記述は(も)“可能”なので、手続き型でもアルゴリズムの方を再利用することは試みられ、実現できている。それがフレームワークとその究極形の一つとしてのNarrow Interfaceパターンである。実際Java8での関数型サポートは、このNarrow Interfaceパターンに立脚している訳だ。
 
◆以上