プログラミング言語の三つの分類

 さて数あるプログラミング言語をその特徴から分類してみましょう。私が注目するプログラミング言語の分類基準は,(1)コンパイラ型とインタプリタ型,(2)型の扱い,(3)ベースとなる考え方(計算モデル)です。

 まずは,コンパイラ型とインタプリタ型です。コンパイラ型の言語はプログラマの書いたプログラムを読み込み,それを全部コンピュータが理解できる命令列に変換してしまいます。変換されたプログラムはコンピュータによって直接実行されますから,プログラム実行時のコストは不要です。一方,インタプリタ型の言語はプログラマが書いた元のプログラム(ソースコード)を逐一解釈して実行を行います。実行が間接的になるぶん,速度は期待できませんが,プログラムを修正してすぐ実行できる,一行プログラムを入力して,その実行結果をすぐに返してもらう対話的な処理が可能などのメリットがあります。

 正確に言えば,コンパイラ型かインタプリタ型かというのは言語処理系の性質であって,言語そのものの性質ではありません。例えばLispは早い時期からコンパイラとインタプリタの両方を提供していましたし,インタプリタ型言語と考えられているBASICもダートマス大で誕生した時の最初の処理系はコンパイラ型でした。しかし,代表的な処理系がインタプリタであり,言語設計者がインタプリタ型処理系を念頭に置いて設計した言語(Lisp,Smalltalkなど)や,実行モデルが実際のコンピュータとはかなり異なっているためインタプリタ型の方が都合が良い言語(SNOBOL,Prologなど)は,やはりインタプリタ型言語として捉えてもよいように感じます。このようなインタプリタ型の言語は,実行効率よりも開発効率や柔軟性を目指すタイプの言語であると考えることができます。

 60年代までの言語のほぼすべてと,80年代までの言語の多くはコンパイラ型言語でした。例外はLispくらいでしょうか。Lispはかなり早い時期からインタプリタ型処理系を持っていました。しかし,その他のインタプリタ型言語が数多く登場しはじめるのはずっと後になってのことです。というのも,それまではコンピュータの利用方法の中心は,計算センターのようなところにある大型コンピュータにプログラム実行を予約するというバッチ型処理で,対話的処理は当然不可能ですし,インタプリタによる効率の低下も許容できなかったのです。

TSSがインタプリタを推進した

 時代が進んでTSS(タイム・シェアリング・システム)によって対話的な処理が可能になったり,あるいは一人でコンピュータを占有できる時代がやってきて初めて,インタプリタ型処理系という考えが実用的になりました。

 このようなインタプリタ型処理系を前提とした言語としては,Lisp,SNOBOL,MUMPS,Prolog,Smalltalkなどがあります。SNOBOLは文字列処理,MUMPSは医療用プログラムの開発を目的として開発されました。Prologは述語論理を計算モデルに採用した言語です。この時代にはPrologのほかにも人工知能向けを目指したたくさんの言語が生まれましたが,現在まで生き残っているものはほとんどありません。SmalltalkはGUIをベースにした対話環境とともに登場しました。インタプリタ型であるのは必然であると言えましょう。

 インタプリタ型処理系には先に述べたように,対話型環境を実現でき,プログラムの修正から実行までの時間が短いというメリットがありますが,もう一つ移植性が高いというメリットもあります。コンパイラはコンピュータが直接実行できる命令列を生成するため,必然的にコンピュータの種類に強く依存します。ある言語の処理系を違うCPUを使ったコンピュータで使うための移植作業は大変なものになります。一方,インタプリタ型の処理系はCPUに依存する部分がありませんので,OSなどの条件が揃えば再コンパイルするだけですぐに利用できます。さまざまな種類のコンピュータが混在する環境ではインタプリタ型処理系の方が圧倒的に有利です。

 JavaはVM(仮想マシン)という一種のインタプリタを使うことで,同じプログラムがどこでも動くこと(Write Once,Run Anywhere)をスローガンにしていますが,このVMというアイデアもSmalltalkなどで既に採用されていた「枯れた」アイデアです。

間違いを減らせる静的な型

 では,もう一つ別の分類基準,型の扱いという観点からプログラミング言語を見てみましょう。型とは変数や式などが持つデータの種別です。この観点からプログラミング言語を分類すると,大きく分けて「型無し」,「静的な型」,「動的な型」の三つに分類できます。

 「型無し」とはデータがその種別の情報をまったく持たないことです。ですから,そのデータ(基本的にはビットデータ)がどのような意味を持つかの解釈はプログラムに任せられます。あるデータが整数であるのか,浮動小数点数であるのか,あるいはどこかのメモリー領域のアドレスであるのかは好きに解釈してくださいということです。アセンブラなどはこのような言語です。この考え方は一番コンピュータのデータの扱いに近く,一番コストが安く,自由度が高いのですが,データの扱い方の間違いを検出できませんし,決して人間にとって分かりやすいとはいえません。

 「静的な型」とは,プログラムの式や変数などのデータ型がプログラムの字面を見るだけで分かるということです。「静的」とは「プログラムの字面上の」とか「コンパイル時の」とか「実行時でない」とかいう意味です。静的な型を持つ言語では,データ型に対して間違った操作を行うことはできません。そのような試みはプログラム上の間違い(コンパイル・エラー)として検出されます。静的な型を持つ言語にはFORTRAN,Pascal,Cなどがあります。

手軽で柔軟なシステムを作る

 「動的な型」とはプログラムの字面上には型情報がなく,データの型は実行時にだけ決まることです。動的な型のプログラミング言語ではデータ自身が自分の型についての情報を持っています。このタイプの言語では型の不整合はコンパイル時には見つからず,実行時にはじめて明らかになります。プログラムのコンパイル時にすべて分かる静的な型の方が優れているように感じられるかもしれませんが,動的な型にもメリットがあります。

 一つは手軽にコードが書けることです。動的な型の言語によるプログラムには型宣言がありません。ですから,プログラムは短く,より簡潔になります。また,型について深く悩まずにプログラムが書けるという点もお手軽さには役立ちます。型エラーに関しては「実行時には見つかるから」と楽天的な態度をとることが多いようです。

 もう一つのメリットは柔軟性です。動的な型の言語では式や変数に型がありませんから,任意のデータを渡すことができます。文字列型を受け付ける手続きに「文字列のように振る舞うけど実は文字列じゃない」データを渡してもなんの問題もなく動いてくれるはずです。一方,静的な型の言語であれば,文字列と「実は文字列じゃないデータ」との共通の型を改めて定義して,プログラムを改変する必要があるでしょう。動的な型は変更に対して強い傾向があります。

 ここでは型の扱い方を三つに大別しましたが,実際の言語ではこの三つが混在することは珍しくありません。静的な型を持つ言語の多くはなんらかの形で型チェックの抜け道を用意しています。FORTRANのEQUIVALENT,Cのunionやキャストなどがその例です。また,静的な型を持つオブジェクト指向言語(C++やJavaなど)でも,オブジェクト指向機能として,動的な型(実行時の実際の型)が静的な型(プログラム字面上の型) のサブクラスであることを許す「動的な型」を持っています。

計算モデルは言語の根幹を担う

 さて,「コンパイラ型/インタプリタ型」,「データ型の取り扱い」に続く言語の分類基準は「ベースとなる考え方」あるいは「計算モデル」です。プログラミング言語は大なり小なりその設計者の思想やポリシーの反映ですが,そのもっとも基本的な部分が「計算モデル」です。

 手続き型は,コンピュータの実行手順そのままのもっともベーシックな計算モデルです。このモデルでは計算は「逐次実行」,「条件判断」,「繰り返し」から構成されます。

 オブジェクト指向型言語は,オブジェクト指向をベースにした言語です。オブジェクト指向言語では,オブジェクトの鋳型であるクラスや個々のオブジェクトが持つ機能である「メソッド」を定義できる機能が追加されています。プログラムの構成がクラスやオブジェクトを単位とするだけで,処理そのものは手続き型と大差ありません。現代のプログラミング言語の多くはなんらかの形でオブジェクト指向機能を持っています。オブジェクト指向という考え方のメリットが理解され,定着してきたことを表していると思います。

 関数型という時の「関数」は,単にサブルーチンを意味するC言語における関数ではなく,数学的な関数を意味します。つまり副作用がなく,同じ入力に対しては同じ出力を返します。関数型言語は数学をベースにした強力な言語です。

 論理型は述語論理をベースにした計算モデルです。乱暴にまとめると,述語論理は「~であれば~である」という命題を積み重ねて三段論法的な推論によって計算を行います。論理型は従来の計算機の処理とはまったく違うモデルであり非常に注目されてきましたが,プログラミング言語としての人気を確立したとは言いがたい状態です。関数型や論理型のような数学をベースにしたモデルはかなり強力なのですが,そのぶん普通の人には理解しにくいという課題があるように思います。

 今月はプログラミング言語の歴史と現状について解説してみました。古い時代の話は伝聞によるものなので不正確かもしれません。その場合はぜひ指摘していただけると幸いです。次回はプログラミング言語のデザインや,未来のプログラミング言語について考えてみたいと思います。