雑な出来になりましたが少しでも何かの足しなればと思います。
Fortran の歴史的構造
Fortran は 60 年以上の歴史をもつプログラミング言語であって、その時代ごとのハードウェアやソフトウェアの発展につれて何度か変化してきました。後方互換性(過去の規格との互換性)を重視しているため、現在の視点から見るとにわかに理解しがたい文法などがそのまま存在し続けています。それらの中には、その当時のハードウェア上の制約や環境を知れば、なぜそうなったのか納得のゆく説明のつくものもあります。
Fortran 批判の中には時代錯誤(アナクロニズム)になっているものも多いので、Fortran の歴史的な構造を知って、有益な批判だけを取り出して未来に活かせればと思い、個人的な視点から Fortran の歴史的な構造をまとめてみることを試みました。
図 Fortran の歴史的構造
FOTRAN II は FORTRAN I を内包する。(互換性がある)
FORTRAN IV と FORTRAN II は互換性が無い。
FORTRAN 66 と FORTRAN IV はおおむね同じものである。
FORTRAN 77 と FORTRAN 66 は互換性が無い。
Fortran 90 は FORTRAN 77 を内包する。
Fortran 95 は FORTRAN 90 の修正にあたる。FORTRAN 77 規格の一部を廃止した。
Fortran 2003 以降も、新規格毎に古い規格の一部を少しづつ廃止している。
Fortran 2008 は Fortan 2003 の修正を超えて、CoAarray を導入した。
Fortran 2018 は Fortan 2008 の修正にあたる。
FORTRAN と Fortran
Fortran は、大きく FORTRAN 77 以前の Classical FORTRAN(古典フォートラン)と Fortran 90 以降の Modern Fortran (モダン・フォートラン)とに分けられます。 この Classical と Modern の呼び名は、公式の規格によるものではなく慣例に基づくもので、明確な定義はないと思います。ただ FORTRAN/Fortran の英語表記に関していえば、国際規格の上でも FORTRAN 77 以前のものは大文字で、Fortran 90 以降のものは頭文字以外は小文字で表記して区別しています。
ネット上では 30 年も前の Fortran 90 を Modern とすることを嫌って、Fortran 2003 以降を Modern Fortran とすべきとの意見も見受けますし、また FORTRAN 77 より古い規格は、もう処理系もあまりないから Classical は実質 FORTRAN 77 だけでいいという考えもあります。これらは狭義の定義と見なせますが、本記事では大雑把に Fortran 90 以前以後で分ける定義を取ることにします。
Classical FORTRAN
Classical FORTRAN は、大きく分けると米 IBM 社固有の規格だった時代と会社の枠を超えて米国規格・国際規格になった時代に分けられます。
IBM 社固有の規格だった時期も、ジョン・バッカスの率いるグループが 704 型計算機固有の言語として開発していた時期と、バッカスの手を離れハードウェア依存性を無くして IBM 社の計算機共通の言語として開発した時期に分かれます。
この後、米国規格・国際規格として IBM 一社のみならずベンダーや国籍を超えた共通の普遍的な言語とする試みがなされます。FORTRAN 66 は IBM 社の FORTRAN IV を元に互換性のための共通の核部分をまとめたもので、FORTRAN 77 は互換性のために共通すべき拡張機能をまとめた形になっています。
IBM 固有の規格
FORTRAN I
FORTRAN は、ジョン・バッカスが上層部に具申して IBM 704 計算機用に開発が始まったもので、実現を疑う逆風の中、バッカス率いる小グループで開発が進められました。
真空管式であった 704 計算機の特徴としては、浮動小数点数計算用の専用ハードウェアを商用機として初めて実装されたことと、アドレス計算用のインデックスレジスタが充実していたことが挙げられています。専用ハードウェアにより浮動小数点数計算が高速化されたため、浮動小数点数計算以外のコードを最適化することが重要になりました。FORTRAN は配列というデータ構造を言語組み込みのデータ型として定義して、特にループ計算での配列要素へのアクセスをインデックスレジスタを活用することで、人間が書くアセンブリプログラムと同レベルの最適化を実現しました。
Fortran は抽象データ構造として配列しか用意されていないことを批判されたりもしますが、Fortran が最も得意とする DO ループによる配列計算の最適化は原初に遡ることが分かります。
変数の型付け(浮動小数点数型、整数型[固定小数点型]の二種)、頭文字での型判別(暗黙の型宣言)、FORMAT 文による型解釈も当初からあり、ファイルの FORMATTED/UNFORMATTED、SEQUENTIAL/DIRECT の区別も当時の I/O 装置のハードウェアに由来しています。
FORTRAN II
FORTRAN I の成功を受けて改良型の FORTRAN II の開発がすすめられます。ここで制御の抽象化として SUBROUTINE と FUNCTION の副プログラムが導入され、あわせて記憶領域の共有化のため COMMON 文も導入されます。SUBROUTINE や FUNCTION は独立に個別にコンパイル可能で、オブジェクトだけを集めて実行形式にリンクできました。
Classical な FORTRAN は、コンパイル時に副プログラムのインタフェースのチェックをしないので、ライブラリを呼ぶときに引数の数や型を間違えてリンクして滅茶苦茶な結果がでる事がよくあり、批判の対象となりました。独立コンパイル (independent compile) に対して、インターフェース情報を含める分割コンパイル (separate compile) が登場し、Fortran 90 以降 Fortran にも導入されました。
真空管式の 704 計算機は記憶容量にも乏しく動作も不安定だったため、数百行に及ぶ(当時としては)長大なプログラムを一括でコンパイルするのが困難であったことを考えると、オブジェクトだけを束ねたライブラリの発達を促したことを考えると画期的なものでした。
また今では悪口しか聞かない COMMON 文もルーチン間の記憶領域の受け渡し機能の他に、記憶容量が乏しい時代に動的再割り付けが無い時代に記憶領域の再利用を可能にする画期的な機能でした。(EQUIVALENCE 文も同様)
版によっては二要素の実数配列を利用して倍精度計算、複素数計算などが変則的にできました。論理演算、サブルーチン・関数の引数化も変則的にできたようです。
FORTRAN III
FORTRAN III は、ハードウェアを最大限活用するために 704 固有の機械語に対応したインライン・アセンブラを組み込んだものとして開発されました。しかし、FORTRAN の成功を受けて IBM の各種計算機に FORTRAN を用意することになった関係で、FORTRAN からはハードウェア固有の部分を切り捨て、I/O 装置なども抽象化してハードウェアに依存しないものにすることとなり、正式にはリリースされず破棄されました。
この決定も絶妙なもので、ハードウェアの有効利用に強い動機をもって誕生しながらも、ある階層でハードウェアを抽象化することで、ハードウェアの最大活用を犠牲にしても普遍性を優先しています。このバランス感覚は、現在に至るまで保たれていると思います。
この前後、FORTRAN 開発はバッカスのグループを離れ IBM のより大きな部門が担当することになっています。そうして普遍性の上がった FORTRAN IV が開発されるようになります。
FORTRAN IV
変数の型として論理型、倍精度型、複素数型が新たに導入され、これに伴い変数の型宣言が出来るようになりました。ただし実数型と整数型は引き続き暗黙の型宣言がなされていることになりました。文字型はありませんが、他の型の変数に文字列を突っ込むことで変則的に扱うことが出来ました。
FORTRAN IV は当時としては肥大化した大きな処理系とされました。上に述べた理由もあって FORTRAN II との互換性は失われています。
米国規格・国際規格
FORTRAN は非常な成功をおさめ、広く普及するとともに、ソースプログラムの可搬性を目的とした規格化・標準化が求められるようになりました。
FORTRAN 66
FORTRAN の普及に伴い、IBM 社以外も独自の拡張をいれた FORTRAN 処理系を用意するようになり、互換性が問題になりました。そこで普遍性を持たせるために IBM 社のオリジナルの FORTRAN IV を元に、ASA (American Standards Association) [後の ANSI (American National Standards Institute)] の定める工業規格として標準化が行われることになりました。
プログラム言語という抽象物が工業規格となるのは FORTRAN が初めてで、どのように定義すべきか多大な苦労があったようですが、数年かかってまとめられました。FORTRAN 66 に限らず年号を後ろにつけたという名は俗称で、規格上の正式名称は FORTRAN だけのようです。この ANSI 規格を元に ISO 国際規格などが定められています。
FORTRAN 66 の規格は既存の処理系に共通する公約数的な核部分を定める方向で進められ、ほとんど FORTRAN IV そのままになりました。この頃既に規格を凍結してこれ以上 FORTRAN に投資せず、新しい言語に移行すべきだという意見が出ています。
FORTRAN 77
FORTRAN 66 規格以後も FORTRAN に対する機能拡張の標準化が求められ、これに伴い新しい FORTRAN 規格を制定することになりました。今回は、各種の拡張機能をどんどん取り入れる方向で進められたようです。実際に、alternate return、entry 文、実数の DO LOOP 変数など、その後廃止された機能が沢山導入されています。
ところが丁度この頃、構造化プログラミングが唱えられはじめます。(ダイクストラの「Go To Statement Considered Harmful」が 1968 年、ACM の GO TO 討論会が 1972 年、クヌースの「Structured Programming with go to Statements」が1974年)
この時点では構造化プログラミングで実行効率の良いプログラムが書けるのか、一般に懐疑的だったこともあって、構造化向きの構文を入れるかまとまらず、最終段階で BLOCK IF 構文だけが取り入れられることになりました。これも微妙というか絶妙な判断で、これすらなければその後 FORTRAN 77 が残ることは難しかったと思われます。
FORTRAN 77 では、新たに文字型変数が導入され文字列処理の方式が変更になりました。また評判の悪かった DO LOOP の実行回数に関する挙動を変えたたり、従来 SUBROUTINE や FUNCTION の局所変数を静的に確保していたのを、スタックを前提に動的に取るようになったことなどから、FORTRAN 77 は FORTRAN 66 との互換性を保っていません。
Modern FORTRAN
Modern Fortran は、Classical FORTRAN に構造化と並列化の機能を入れて再構成したものになっています。構造化は ALGOL-PASCAL-MODULA-Oberon 系統のデータ・制御・モジュールの構造化、並列化はデータ・パラレル型の計算を念頭にハードウェア的には array processor-共有メモリ型(自動並列化)―分散メモリー型(明示的並列化)に、ゆるく対応した形で文法が拡張されたと考えると理解できると思います。
歴史的には大きく二つに分けられます。一方はもっぱら静的な Fortran 90/95、他方は動的な機能を強化した Fortran 2003/08/18 です。ここで静的・動的というのは、コンパイル言語では型やサイズの決定のタイミングにコンパイル時と実行時という二つのフェーズがあるうちで、静的はコンパイル時に確定するもの、動的は実行時に確定するものという意味になります。
Fortran 90/95 は配列のサイズを動的に定めることが出来ますが、明示的にサイズを指定して確保する必要があるため柔軟性に欠けています。Fortran 2003/08/15 ではオブジェクト指向(OO [Object-Oriented]) が導入され無制限型(unlimited type/class)変数が導入されたり、代入による左辺変数の自動再割り付けがなされるなど、動的な機能が強化されています。
動的な機能を強化すると、ソースコードレベルでの記述力は上がりますが、実行時の最適化が困難になるというトレードオフがあります。現在のコンピュータでは、記憶領域間の移動が律速段階になっていて、階層キャッシュ構造をとってこれをカバーしていますが、キャッシュを最大限活用するためには連続した記憶領域を細かく制御する必要があり、静的配列と明示的ループ変数による制御が、適度な抽象化を保った上で、有効に機能します。ここに HPC 分野で classical な FORTRAN 77 が未だに好まれて使われる理由があると思います。
Fortran90/95
FORTRAN 77 から Fortran 90 までに大きな時間間隔が開いてしまった反省から、5 年を目安に大きな変更と小さな修正を交互に繰り返すことになりました。Fortran 95 は不具合修正を主とする小さな変更になっています。
Fortran 90
Fortran 90 は、classical FORTRAN に構造化プログラミングと配列演算を持ち込んだもので、これまでの面目を一新するものになっています。一方で、FORTRAN 77 規格を完全に内包しており、完全な後方互換性を保っており、微妙なバランス感覚を示しています。
構造化プログラミングとは何かという定義は極めてあいまいなもので調べてもはっきりしないのですが、設計論としてはトップダウンによるサブルーチンや関数への分割、プログラム言語上はデータの構造化と制御の構造化の構文の導入になるようです。(いわゆる GO TO 文追放論は、制御の構造化を切り出したものになっています。)
Fortran 90 ではデータの構造化として派生型(他言語での構造体)が導入されました。
制御の構造化としては、従来からある BLOCK IF に加え、DO WHILE、CYCLE、EXIT、END DO や SELECT CASE が導入されました。なお Fortran では式が値を持たず、関数が副作用を持つことも嫌うので、他言語と違って WHILE 構文の適用範囲が狭く使い勝手が悪くなっています。REPEAT UNTIL 型の構文は存在しません。
また subrotuine や function を大域変数と共に束ね、分離コンパイルを可能にする module 構造を導入したことで PASCAL や C より若干進んだところもあります。
配列演算は全配列要素を一括で操作できるもので、配列操作の抽象度が上がっています。これは元々は並列ハードウェアとして、配列の一要素毎に演算プロセッサを用意して一括で演算を行う、ILLIAC IV や Thinking Machine に代表されるような array processor を念頭においたものでした。配列に対する MASK 構文や CSHIFT/EOSHIFT 関数、PACK/UNPACK 関数などは、トーラス状に結ばれたフラグ付きの array processor をイメージしないと存在意義が理解しにくいと思います。ところが array processor はベクトル型のプロセッサに敗れて、並列アーキテクチャとしては歴史のかなたに消えてしまっています。
Fortran 95
Fortran 95 は Fortran 90 に対する不具合修正の意味合いをもつものになっています。最適化の妨げになる古い文法要素を廃止したため FORTRAN 77 との互換性が若干失われています。これ以降の規格でも少しづつ古い文法要素を廃止していっているため、厳密な意味では互換性は保たれません。
動的配列の解放が SUBROTUINE や FUNCTION を抜ける時に自動で行われるようになり、ゆるいガベージ・コレクションがなされるようになりました。
並列計算を行う上で subroutine や function の純粋性が重要になりますが、それを保証する pure 修飾子が導入されています。関数型言語ブームが起こってようやくその重要性が認識されるようになった気がします。
Fortran 2003/08/18
5 年を目安に大小変更を繰り返すはずが、Fortran 2008 では Fortran 2003 への修正にとどまらず、CoArray と呼ばれる分散並列アーキテクチャ向き並列計算命令の追加という大きな改変が行われました。その後の Fortran 2018 は 10 年の間隔をあけながらも、Fortran 2003/08 の不具合修正のような小さな改変になっています。
Fortran 2003
Fortran 2003 は、世の流れを追ってオブジェクト指向に対応した文法要素を導入しており、それに伴い動的なプログラミング機能を充実させています。しかし、あまりに改変の規模が大きかったため処理系の実装が遅れました。このため Fortran 2008 規格が定められる頃になっても、完全な Fortran 2003 処理系がほぼ無いような状態でした。
Fortran 2003 では、派生型の拡張 (extend) によってインヘリタンスを、type-bound procedure によってメソッドを実現しています。最近の世の流れは、インヘリタンスやメソッドを否定する方向に向かっていて居心地悪い気にさせます。
IEEE754 関連の機能や、C 言語との相互運用機能、文字変数の可変長機能、parameterized type など処理系への負荷が大きい割に使いづらい機能もあり、肥大化してバランス感覚の悪い印象を受けます。
Fortran 2008
本来は Fortran 2003 の不具合修正の位置づけのはずですが、並列計算機能として CoArray が押し込まれて、その位置づけを逸脱しました。
CoArray は PGAS (Partitioned Global Address Space) モデルに基づき、MPI より簡便に分散並列型アーキテクチャ向きのプログラミングを行うことを目的としています。PGAS は 2000年代に入った頃に米政府の肝いりで様々な言語で試されましたが、現在に至るまですべてパッとしない状況です。PGAS は GPU のような装置には向かないモデルで、この方面にも対応できていません。
Fortran は MPI 主流化以前には、共有メモリ型のモデルでのコンパイラによる自動並列化を目指して、HPF 等を推進してきましたが(日本も熱心に)、データ移動が律速になっている分散メモリー型の場合自動並列化は難しく、人間が明示的にデータ移動を制御する MPI に勝てずに廃れています。
当初 Fortran コンパイラによる自動並列化は、メモリー共有型のモデルに基づき、既存のシリアル実行用のプログラムにコンパイラ指示行のコメントを付加する形式で推進されました。ここでも指示行の規格が乱立したため、これを統一する目的で OpenMP が生まれました。しかし OpenMP もパッとしない性能で、共有メモリー型においても MPI の方が高い性能を出すため MPI の独走を許しました。OpenMP は C 言語にも採用され、最近では C 言語側が主導して規格を制定しています。
Fortran 2018
Fortan 2018 は、Fortran 2003/08 の不具合修正を主とした小さな改変になっています。とはいえ Fortran 2008 に拙速で押し込んだ CoArray の機能拡張なども入っています。
並列プログラミング向けの拡張において Fortran は賭けに負け続けている気がしますが、他の並列言語も負け続けているのでなんとかなっています。Fortran はデータ・パラレル型の並列化を指向していますが、最近の他言語はタスク・パラレル型の並列化を指向しているのでまだまだ賭けを続けても大丈夫だと思います。
オブジェクト指向においても、Fortran は中途半端さを指摘されていますが、オブジェクト指向ブームも峠を越した感もあり、このまま乗り切れる気がします。
まとめ
歴史的な構造を振り返ってみると、Fortran は同時代のハードウェアを絶妙なレベルで抽象化して普遍性を保っており、高い効率でハードウェアを利用しつつもアーキテクチャ衰退の道連れにならずに、しぶとく次世代に残り続けている気がします。
ハードウェア直結命令を捨て、インライン・アセンブラも捨てながらも、ベクトル計算機やキャッシュ・アーキテクチャを活用できる程度に抽象化を抑えてきたバランス感覚を理解し保ちつづけてゆくことが、今後とも大切なのではないかと思います。