Goならわかるシステムプログラミング第2版が出たので書評しますね
少し前になりますが、3月23日に、渋川よしきさんの著された「Goならわかるシステムプログラミング 第2版 」が発売されました。初版と比べてかなり加筆されておりパワーアップしているので、初版をすでにお持ちの方でもさらに興味深く読むことのできる内容に仕上がっている、というのが第一印象です。
残念ながら初版発売時に記事にする機会がなかったのですが、あらためて今回書評したいなと思いましたので、徒然書いていきたいと思います。
この本は実はシステムプログラミングの本ではないかもしれない
「システムプログラミング」とは何でしょう。正直私にもわかりません。その語をはじめに思い浮かべた人は、プログラミングという概念のその中にあえて「システムプログラミング」という分類を作ろうと思い至ったということですから、きっと「非システムプログラミング」というものもあるということでしょう。知らんけど。しかし、これは本書の位置づけを議論する上で重要な視点となります。
書評する立場ながら大変申し訳ないことに見識もないもので、ついWikipediaを見てしまうわけですが、探してみると「Systems programming」という記事が見つかりました。これが最初に作られたのは2003年とのことなので、ある程度信頼を持って眺めても良さそうではあります。当該記事の冒頭から引用してみます。
Systems programming, or system programming, is the activity of programming computer system software. The primary distinguishing characteristic of systems programming when compared to application programming is that application programming aims to produce software which provides services to the user directly (e.g. word processor), whereas systems programming aims to produce software and software platforms which provide services to other software, are performance constrained, or both (e.g. operating systems, computational science applications, game engines, industrial automation, and software as a service applications).
この記事によれば、「アプリケーションプログラミング」という概念があり、「システムプログラミング」との違いは「アプリケーションプログラミングはユーザーに直接サービスを提供するものである一方、システムプログラミングはその他のアプリケーションにサービスを提供するものである」とのことです。なるほど、「非システムプログラミング」というのは「アプリケーションプログラミング」のことなのかもしれない。そうすると、システムプログラミングはオペレーティングシステム自体、またはそれに関わるもののプログラミングという言い方が暫定的にはできるかもしれない。
システムプログラミング言語とは?
ところで、「システムプログラミング言語」という言い方もあります。これはこれで独立した記事「Systems programming language」が存在するのでそちらも眺めてみることにしましょう。
A system programming language is a programming language used for system programming; such languages are designed for writing system software, which usually requires different development approaches when compared with application software. Edsger Dijkstra refers to these language as Machine Oriented High Order Languages, or mohol.
「システムプログラミング言語というのは、アプリケーションソフトウェアを開発するのとは異なるアプローチが要求されるシステムソフトウェアを記述するのに適した言語である」と述べています。そして、そのような言語をエドガー・ダイクストラは「機械志向高階言語」と呼んだと述べています。(参考: https://www.dijkstrascry.com/node/121 )
果たして「Machine Oriented High Order Languages」(MOHOL) という呼称を思いついたのがダイクストラであったのかどうかは不明ですが、当該記事によればこの呼称は1973年に北オランダで開催されたIFIP working conferenceのダイジェスト [1] に登場するようです。Software: Practice and ExperienceのVolume 7, Issue 1 (1977) に掲載されたS. H. Valentineによる当該ダイジェストの書評を引用します。なお、時代背景の理解を助けるために記すと、Cが生まれたのは1972年といわれています。
Machine Oriented Higher Level Languages turned out to be a slightly contentious title. There was a good deal of discussion about the nature of Machine Oriented Languages (MOL’s) and their relationship to System Implementation Languages (SIL’s). Some of the desirable features of the latter were identified, such as high implementation efficiency and the ability to manipulate complicated data structures. It was admitted that these features may be of interest in other programming contexts as well, and several of the proposals for SIL design throughout the book included features which would be of general applicability. System implementors, however, are perhaps as a group more willing than others to learn a new programming language to achieve their aims. They may also in the near future be the only group who are prepared to accept any machine-dependency at all.
There seemed to be general agreement that assembly languages should be abandoned, and it appears that the manufacturers share this view. There were clear disagreements on what should replace them. A paper by Barreveld describes ALIAS, a machine-independent language intended for minicomputers, with a high-level syntax but low-level semantics, with no typing of data and an explicit dereferencing operator inspired by BLISS. Other language designers have gone for fully typed semantics. These tend to be influenced by existing high-level languages like Algol 68 and PASCAL. A paper by Conradi and Holager makes a study of MARY, a language of this class, on the question of the typing of data. There were some sharp disagreements expressed on this issue in the final panel discussion.
この一文は本書に立ち返っても非常に貴重な洞察を与えます。引用の抄訳を順に記します。
まず最初の段落ですが、
機械志向高水準言語 (Machine Oriented Higher Level Languages) というのは、いささか議論を呼ぶ表題であった。 MOL (機械志向言語) の性質と、それらのSIL (システム実装言語) との関係性については、これまでさまざまな議論があった。だが、後者の望ましい特徴というのはいくつか判明している。たとえば、複雑なデータ構造を操作する効率や能力といったものである。このような特徴はプログラミングにおける他の側面でも重視されうるものであり、原著を通して述べられたSILの設計に関する提案の中にも、より広範に適用可能なものがある。集団としてのシステムプログラマ (システム実装者) は、自らの目的のために、他 (の実装者) と比べて新しいプログラミング言語を学ぶことに積極的であるといえよう。また、システムプログラマは、近い将来機械依存性を許容しうる唯一の集団となるかもしれない。
MOLこそがシステムプログラミング言語を指した語であるのかと思いきや、「システム実装言語」という言葉が出てきます。システム実装言語を文字通り解釈すればシステムプログラミング言語となるので、少し混乱してきました。SILの正体が何であるかは別として、MOLとの違いは「複雑なデータ構造を操作する力」であるようです。操作できるというからには、それに対応する記述力が備わっていると推測しても大きく間違っていないでしょうから、「複雑なデータ構造を記述する力」と言い換えても許されるのではないでしょうか。
この部分、現代からすると「何を言ってるんだ」となりそうなものですが、当時、オペレーティングシステムの複雑さに比べれば、アプリケーションは非常に単純であったという点を考慮しなければならないでしょう。システムプログラミングは相対的に認知負荷が高い行為であり、それを軽減することのできる (高水準な) プログラミング言語があるならばそれを選びたいという動機はシステムプログラマに強くある一方で、本質的にシステムプログラミングはマシンアーキテクチャの詳細にも触れる必要があるものであるということです。
実際、その時代は相反するとも言えるそれらの要件を両立する挑戦として、アカデミックにもソフトウェア産業としても盛んに実験が行われていたのです。2002年にSoftware: Practice and Experienceに掲載されたThe BLISS programming language: a history.という記事 [2] には、次のような記述があります。
In the late 1960s and early 1970s, the use of high-level languages for systems programming was highly controversial. Most systems had very limited amounts of main memory; indeed, even so-called large systems generally had hardware maximum address space limitations of 16 or 18-bits of addressable memory—and no virtual memory support either. ‘Size is the goal’ for both code and data often summarized one side of the debate. Systems programmers wanted lots of control over every instruction and every cycle and every bit of memory used. The advocates for using high-level languages cited the potential for improved productivity and reduced error rates through the use of structured programming and data abstraction. However, the languages that seemed expressive enough for the job at the time, languages such as PL/I or Algol 68, were not viewed as suitably efficient. Arguments raged over whether it was even conceivable, let alone practical, for compilers to produce code that was within some small incremental percentage of what could be achieved by good programmers. No one ever picked zero. Lots of experimentation was in progress throughout the computer industry seeking languages with the right blend of high-level features, low-level control, ease of compiling to efficient code, and improved optimization technology. The few, then contemporary, languages that fit these goals according to the earliest paper on BLISS were EPL (‘Early PL/I’, a bootstrapping PL/I subset used for Multics), Burroughs Extended Algol, PL360 and BCPL.
訳:
1960年代後半から1970年代前半にかけて、システムプログラミングに高水準言語を採用することは、かなり否定的にとらえられていました。多くのシステムは非常に限られたメインメモリしか備えておらず、大型システムと言われるものでも、16ビットから18ビットのアドレス空間のメモリしか扱えないハードウェアから成っており、仮想メモリのサポートもありませんでした。当時の議論の半分はコードにしてもデータにしても「サイズこそがゴール」という言葉に尽きます。 他方、高水準言語を推す側は、構造化プログラミングやデータ抽象化を通じて高い生産性とより低いバグ発生率を達成できる余地があることを強調していました。しかし、当時システムプログラミングにおいてそのような言語にも十分表現力があると考えられていたにもかかわらず、PL/IやAlgol 68といった言語は目的に照らして効率的とは考えられていませんでした。議論は白熱し、実用的かどうかの範疇を超えて、果たしてどの程度、コンパイラの生成するコードが、腕のたつプログラマーの書く (アセンブリ言語の) コードに肉薄できるか、というところにまで至りました。しかし誰一人違いをゼロとは言いませんでした。 高水準言語の特徴、低水準言語の取り回しの良さ、効率的なコード生成の容易さ、より進んだ最適化技術がバランスよく混ざった言語を探す試みがコンピュータ業界全体で行われていました。その中でも数少ない、現代にも残る言語の中で、BLISSの初期の論文にその目的を満たすものと述べられているものはEPL (「Early PL/I」という、Multicsのブートストラップに用いられたPL/Iのサブセット言語) と、バローズ社の拡張Algol、PL360、それからBCPLでした。
最初のMOLとSILの対比が何を意味するのかについての答えは、この部分の参照にあるように、前者が低水準言語、後者が高水準言語、というように考えてよさそうです。MOとHLの両方の性質を兼ね備えた、この議論の時点ではまだ存在していなかったMOHLを模索していたということです。
さて、一旦もとの文章に戻ります。
アセンブリ言語はいずれ放棄されるべきである、という見解については広く合意があるように思われたし、また、(計算機) メーカーもそれを支持しているようだが、一方で何でもって置き換えるべきかという点では明らかな意見の相違がある。Barreveldの論文のALIASは、BLISSの影響を受け、型なしデータと明示的なデリファレンス演算子を持つという、高水準の文法と低水準のセマンティクスを兼ね備えた、ミニコンピュータ向けの機械非依存言語である。他の言語設計者は完全に型付けされたセマンティクスを好んでいた。そのような言語にはAlgol 68やPASCALといった既存の高水準言語に影響されたものが多い。ConradiとHolagarの論文は、この種のMARYという言語について、データの型づけの有効性を検討するものである。この問題については最後のパネルディスカッションでかなり明確な意見の食い違いが見られた。
BLISS は1969年にカーネギーメロン大学で開発され、一時はベル研究所でCとシステムプログラミング言語の座を争ったという経緯のある言語ですが、この言語は型という型のバリエーションを持たず、すべての変数も定数もアーキテクチャ依存のフルワードの整数値 (例えばPDP-11をターゲットとするならば16ビット、PDP-10をターゲットとするならば36ビット) を格納するという仕様でした。また、これは当時のミニコンピュータの主流アーキテクチャに由来すると思われるのですが、すべての変数は何らかのメモリ空間上のアドレスに対応しており、変数の表す値はその値ではなくアドレスというセマンティクスを採用していました。そのため、変数の値を取り出したり、値を格納する際 (デリファレンス) には .
記号を変数の前に置く必要があります。高水準の文法といえども、セマンティクスは非常に原始的で、かつアーキテクチャ依存であり、これが生産性の高い言語と考えられていたということは非常に興味深い点です。
そのような背景があるため、Algol 68やPASCALといった系譜に属する型付けを持つ高階言語が当時システムプログラミングの分野で花開くことはありませんでした。そのため、1980年にAdaという形でアメリカの軍事規格に採用されたことは、当時のコンピュータ業界に大きな驚きをもって迎えられることになります。どのくらい議論を呼んだのかというと、トニー・ホーア *1 がチューリング賞の記念講演で名指しで信頼性について疑問を呈する発言をするほどでした [3]。宇宙開発や軍事産業などの分野で複雑な機械制御のためにコンピュータが組み込みという形でその適用の場を広げることになると、現在のようにフラッシュメモリを搭載することでファームウェアをインサーキットプログラミングするというような概念がない中で *2 製造時点でのコードから一切変更できない組み込み分野でいかに品質を高めるかということが課題となりました。アメリカ国防総省は当時450種類もの (多くはアーキテクチャ依存で、アーキテクチャの保全が終了した時点で放棄された) 言語が兵器の制御に用いられていることを憂慮し、1975年にHigh Order Language Working Group (高階言語ワーキンググループ) を結成し、それらにとって代わる新しい言語の模索を始めました。もともと彼ら彼女らは既存の言語の採用を検討していましたが、最終的に要件を満たすものがないと判断し、新規に言語の開発を行うことを決断します。その結果誕生したのがAdaでした。Adaのような高階言語が「ビジネス」になったということがそうした言語の研究開発を促し、(この文脈にはSmalltalkなどの動的型付きオブジェクト指向言語が含まれます) 最終的にJavaのようなガベージコレクションをランタイムに備えた汎用プログラミング言語の開発につながったのではないかと、個人的には思います。
そしてGo
1977年という時代を考えると、ここでCについての言及が少しもないのは不思議に思われたかもしれません。それはというもの、Cは1978年にブライアン・カーニハンとデニス・リッチーによる書籍が出版されるまではずっとベル研究所のインハウスプロジェクトであり、様々なソフトウェアプロジェクトで使われたといえども、そこで成熟されてきたインハウスプログラミング言語であったということです。GoがGoogleのインハウスプロジェクトであったことを振り返るととても示唆的に思えるところがあります。
これは別の機会に述べたことがあるのですが、Goのソースコードレポジトリの最初のコミットは、実はGoではおろかCでもありません。
hello, world R=ken DELTA=7 (7 added, 0 deleted, 0 changed)
Brian Kernighan committed on Jul 19, 1972
意味深なコミットですが、これはBと呼ばれるC以前にブライアン・カーニハンによって開発された言語によって書かれた Hello World です。Bは既出であるBCPLと呼ばれる言語に強くインスパイアされた言語で、BCPLの持つ特徴であるブレース ({
}
) によってブロックを表現する仕様などで影響を受けています。 ((なお、BCPLのコメントは //
を前置することで記述できますが、この仕様はCには引き継がれず、C++で復活を遂げることになりました。))
そして次のコミットは
ですが、これは前コミットでBで書かれた内容をCに書き直したものとなっており、GoがBから始まるベル研究所のプログラミング言語のブートストラップの系譜にいるということをまるで示唆するかのようなイースターエッグとなっています。
Goにようやく話が戻ってきたところで、システムプログラミング言語とは、システムプログラミングとは何なのか、という問いに戻ってみたいところですが、歴史を振り返って得られた知見というのは、そこに求められる性質というのは、以下に集約されそうだということです。
- 抽象的なデータ構造の記述力 (Goでいうstructやinterfaceを含む型システム)
- 構造化プログラミング (Goでいうスタックフレームに基づく関数スコープ)
- ハードウェアアーキテクチャに基づいた操作の記述が可能 (Goでいう豊富なプリミティブ型、unsafe)
- 上記を担保しつつ、コンパイラが高効率なコードを生成できること
それからもう一つ、求められる性質自体は、時代時代の要請によって変化しうるという点です。ここでGoは明らかに上記4つの性質を備えながら、Cにはない以下の特徴を持つことになりました。
- 特定の文字エンコーディングを前提とする文字列型
- スライス
- ガベージコレクション
- 並行処理 (goroutine) および補助プリミティブ (channel)
これらのうちの多くは、1990年代から2000年代のコンピュータ業界に大きな足跡を残し現在も広く使われるJava、それからPythonをはじめとする多数のスクリプト言語の影響がないとは言えない特徴です。特に、「認知負荷を低減し、高い作業効率が達成できる」という側面は、1960〜70年代と比べ飛躍的に実装面積あたりの計算性能が向上し、同時にコストが低下したことでコモディティ化を達成した高性能な小型計算機の存在を強く反映したものといえます。つまり、低水準と高水準の両立という自転車置き場の議論は計算資源に乏しかった時代の産物であり、ムーアの法則は言うまでもない製造技術の進歩が自然と本来の「システムプログラミング言語」を我々にもたらした、ということです。
小型化、低コスト化した高性能な小型計算機がワークステーションやパーソナルコンピュータという呼称で広く一般にマーケティングされるようになり、システムプログラミングに求められる言語的、ランタイム的要素のあり方を変容させましたが、このことは同時にアプリケーションプログラミングとシステムプログラミングの境界も変化させてきたと個人的に考えています。それが象徴的に現れているのがシェルの実装です。ベル研究所で開発されたBourne Shellの最初のバージョンは1979年でしたが、Korn Shellは1983年、Bashは1989年、Z Shellは1990年と着実に新しい世代が誕生しており、オペレーティングシステムの単なる外層という役割を超えて、システムオペレータの生産性という観点での進化を遂げてきました。その意味で、現代的に「低レベルアクセス」ということは、必ずしもマシンアーキテクチャを直接触ることのできるような過去の意味での低レベルのみを意味せず、抽象化層を経由しないでシステムコールのようなOSのプログラミングインターフェイスに触れることをも低レベルと呼ぶことは、そう大それたことではないといえるのではないか、と思っています。
「Goならわかるシステムプログラミング」はどこに流れ着くか
上で述べたことを一言で言えば、利用可能な計算資源の増加と生産性の追求からシステム全体の複雑性が増し、「低レベル」の枠が広がったことで、そこに含まる概念やトピックも一面的ではなくなってしまった、ということなのですが、その点についていうと、システムプログラミングに関して、何が関心事か、何を知っておかなければならないのか、ということを1冊で横断的に解説するのはとても困難なこととなってしまったように思います。1冊の旅行ガイドで世界旅行を案内するのが無謀であることと同様なようにです。ですから、本書に対してそのような期待を抱くのは、少々筋が違うということになるでしょう。では何であるかというと、著者である渋川よしきさんの、システムプログラミングの世界の探訪記と捉えています。もちろん、訪れたいろいろな地域の詳細はきちんと調査されて、サンプルコードにより裏付けがされている点では、随筆のような旅行記とは異なるのですが。
もし本書を手に取る機会がありましたら、旅行先の歴史や文化の背景を学ぶとより興味深くなるように、コンピュータサイエンスの歴史なども頭に巡らせながら読んでみると、より面白く読めると思います。各章で取り上げる内容について、他の解説も参照し掘り下げながら、理解を深めつつロマンを味わうのも、一般的な技術書にはない楽しみ方かと思っています。
[1] van der Poel, W. L.; Maarssen, L. A., eds. (27–31 August 1973). Machine oriented higher level languages. IFIP Working Conference on Machine Oriented Higher Level Languages (MOHL). DOI: 10.1002/spe.4380070115
[2] Brender, Ronald F. The BLISS programming language: a history. Softw. Pract. Exper. 2002; 32:955–981. DOI: 10.1002/spe.470
[3] Hoare, C. A. R. The Emperor's Old Clothes. Communications of the ACM; Vol. 24 Issue 2. DOI: 10.1145/358549.358561
*1:C.A.R. HoareはGoの並行処理のセマンティクスの礎となっているCSPを発明した人物です。皮肉なことには、Adaの仕様にはCSPの影響を受けたrendezvousと呼ばれるプリミティブが含まれています。なお、この批判についてホーアはのちに謝罪しています。関係ないけどnull参照を発明したことについても謝罪している。えらすぎる。
*2:ちなみにEPROMが発明されたのは1971年です。