インタプリタ言語とコンパイラ言語の共存


Table of Contents

1. インタプリタ言語とコンパイラ言語の共存
なんで?
なんで、技術的に可能なのか
なんで、そんなことするの?

Chapter 1. インタプリタ言語とコンパイラ言語の共存

なんで?

なんで、技術的に可能なのか

個人的な事ですが、始めて書いた UNIX 上のプログラムは、Sun1 の上で動く、D 言語のインタプリタ(Interpreter-D)でした。D 言語とは私が勝手に名前を付けたのですが、(故)Edsger Dijkstra の「プログラミング原論」に出て来る言語です。就職して現場配属して最初の2週間は UNIX 実習と言う事で好きな物を作れと言われたので調子こいて作りました。会社には Interlisp-D という当時最先端の LISP マシンがあった(他にも Symblics とかもあった)のですが、新入社員には使わせてくれなかったので、むしゃくしゃしたのでやった。

共存できるようなインタフェイスが用意されているから、というのが実際の所なのだが、ではなぜそういうインタフェイスが用意できるのか?

式と演算子と制御構造しか無いインタプリタ言語ってないですよね。必ずファイル I/O とかGUI用の組み込み関数がついてますね。そいういう組み込み関数ってどうやって作られているか分かりますか?。当然ですが、コンパイラ言語で作られているのです。だって、最終的には OS とかの API を呼ばなければならないのですから。

だから、インタプリタに組み込んである関数を真似して、自分用の関数を増やせれば良いんです。実は昔はみんなそうやっていました。

Dynamic Linking (あるいは、Dynamic Loading and Linking)という技術が確立される前は、みんな勝手にインタプリタ本体を拡張していました。だって、ソースがあるんだし、勝手に改造しちゃだめ、とか言われてないし。つまり、みんな自分用の perl とか、自分用の emacs とかを作ってから、仕事をしていたのです。自分の使う道具を自分の使いやすいようにカスタマイズするというのは、職人として一人前になるということですから。

M$の支配下に生きていてそれが当り前だと思っている人には分からないかもしれませんが、ソースがあり、かつ、改造する権利がある、ということは、そういう事なのです。単に見た目(スキン)を変えるとか、メニューの項目を追加するとか、そんなチャチなレベルではありません。いざとなれば、OS から自分の好きなように作りかえたっていいんです。そして、それを配っても。これが、free の意味です。タダ(無料)とは本質的に意味が違います。

でも、みんな勝手に拡張していたら、A さんバージョンの perl と B さんバージョンの perl を混ぜるのは面倒です。なんとかする方法が必要になってきました。

そこで、みんなで決めました。拡張関数を作るときのルールです。でも、それで問題が全て解決するわけではありません。

perl がどんどんでっかくなってしまいます。みんなが作った拡張関数を全部搭載したら、perl の実行プログラムの大きさがウン100MBになってしまいます。そんなことしたら、起動するだけで何分もかかってしまいます。

だから、自分の必要な拡張関数だけを搭載した perl を各自が毎回ビルドしなければなりません。めんどくさいです。

そこで、登場したのが、Dynamic Linking という技術です。実行プログラムを作るとき、普通は、ソースコードをコンパイルしてできたオブジェクトコードとライブラリを、リンカがまとめてビルドします。このリンカの仕事を、プログラムの実行中に行うという技術です。

詳しい仕組みはともかくとて、インタプリタの関数は結局はコンパイラで作った関数だということと、Dynamic Linking 技術、の組合せによってインタプリタ言語からコンパイラ言語で書いた関数が呼び出せるのです。

一方、コンパイラ言語の中に、インタプリタを埋め込む、ということもたまにやります。別に難しいことではないです。だって、インタプリタ本体は、結局は、インタプリタの実行プログラム(main())が呼び出してる関数でしかありません(良く、エンジンなどと呼ぶことがある)。

なんで、そんなことするの?

なんで、一つの言語で全部書かないのか?単純な話です。面倒くさいし時間がもったいないからです。

所で、プログラミング言語、というのは何なのでしょう?これは、なぜ、計算言語(Computing Language)ではないか、が答えです。Computer 用の言語なのに、何で Computing Language じゃやないのか。

じゃ、計算機の仕事って何だと思います?計算?ぶー。正解はデータの転送と転送の制御です。

CPU の中には演算器が入っています。機械語の命令を見ても、機械語の命令自体で演算をしているわけでは有りません。単に、演算器にデータを送って、答えが出るのを待ってるだけです。これって、高級プログラミング言語で関数を呼び出しているのと同じです。

つまり、計算機の仕事って、演算やら、入出力やら、いろいろ仕事をしてくれる部品に、適切なデータを適切なタイミングで送り込んでそのレスポンスを待つ、を繰り返しているだけです。ですから、計算機を制御するというのは、演算を制御するのではなく、データの転送を制御することです。演算は道具(小さな部品)に過ぎません。

部品はハードウェアで実現されている場合もあるでしょうし、ソフトウェアとして実現されている場合もあるし、ハイブリッドな場合もあります。なんであれ、実用的なソフトウェアでは、ソースコードの大半は、部品にデータを送り込むことばかりしています。

単に、部品にデータを送り込むだけなら、どんな言語で書いたってそんなに性能差は出ません。だったら、苦労して機械語で書く必要なんて無いです。また、コンパイラ言語で書く必然性にしても、それは速度ではなくコンパイル時の各種チェックの厳密さです。これが一番重要なのです。だから、キャストばかりしているようなC++のプログラムには存在意義はありません。コンパイル時のチェックの厳密さに耐えられない人や、それほど厳密さを要求されない状況だったら、楽なインタプリタで書けばいいじゃないですか。

インタプリタ言語のメリットの一つは、現場で簡単にカスタマイズできることです。何しろソースコードが現場にあるのですから。極端な話、その場で開発してもいいくらいです。お客さんには、開発が済んだからインストールに行きます、と言っておいて、実際には現場に行ってからゼロから作っておきながら、このシステムの開発には一ヵ月かかりました、と言うこともできます。

コンパイラ言語で、現場でカスタマイズするとなると、開発環境を現場の運用用のマシンにインストールしなければならないです。実用プログラムの開発には、沢山のツールキットが要ります。開発環境を組み上げるだけで何日もかかるなんて普通です。それに、Windows とかだと、開発ツール自体がかなり高いです。だから、コンパイラ言語だけで実用プログラムを作るとなると、カスタマイズ可能な項目と、カスタマイズ不可能な項目を分け、かつ、カスタマイズ用に複雑な設定ファイルを作らなければなりません。これは、かなり面倒です。

どうしても、コンパイラ言語で書く必要が有るところだけ、コンパイラ言語で部品を書けば良いのです。性能と信頼性の両立はコンパイラ言語でしか実現できません。Javaなんかじゃ到底無理です(性能と信頼性がソコソコで良い場合は Java は悪くない選択肢です)。更に性能を追求するならばアセンブリ言語も混ぜなければなりません。そういう部品が作れるのが(3,1)な人です。