去る2020年2月、縄文陶芸家にしてC++プログラマという希有な二つの顔を持つ人物がこの世を去りました。 ボレロ村上 (村上原野) 氏です。
正直なところ、陶芸家としての彼の側面については私はほとんど何も知らないに等しいです。 残された作品を見て何かを語れるほど芸術に通じているわけでもありません。 いつか機会があれば見に行こうと思っていた村上さんの作品を目にする最初の機会も、 昨年訪れた追悼展になってしまいました。 ただ、それでも私は、彼自身の登壇発表の資料 であったり、 そして何よりも縄文・陶芸に造詣の深い方々の記された幾つかの文章によって、 彼が一体何を成し遂げたのか、成し遂げようとしていたのかに触れることができています。 この夏にはクラウドファンディングで 作品集 も出版され、誰でも手にとって縄文アーティストとしての村上原野を知ることができます。
翻って、プログラマとしての村上さんが残した足跡は、 果たして C++ マニアでなくとも辿れるようになっているでしょうか。 彼がどのように異色の存在だったか、C++ の知識を前提とせずに書き留めておくことはできないものでしょうか。 この一年こういった疑問が頭を離れなかったので、拙いながら、 一筆残しておこうと思い書き上げたのがこの記事になります。 多忙にかまけて随分と遅くなってしまいましたが、ご容赦ください。
上のような趣旨を私が Twitter でつぶやいた時、いただいた反応の一つが好きだったので引用します。
「constexprをやたらと酷使することで有名だった人」の説明しづらさすこ
— ジャカルタ読み専ブラザーズ (@_oinarisan_) October 7, 2020
たぶん、プログラマとしての村上さんをご存じの方は、皆が皆、 この「constexprをやたらと酷使することで有名だった人」 という評に同意されるのではないかと思います。 これは考えてみると凄いことで、どんな分野であれ、 「○○の人」と満場一致で呼ばれるだけの他を寄せ付けないアイデンティティを確立するというのは、 並大抵のことではないでしょう。にも関わらず村上さんは確かに、「constexprの人」でした。
さて、constexpr とは何か。言葉の字面だけを説明すると、これは "constant expression" の略語で、 プログラムがコンピュータの中で動作している最中にも一切変わらない (constant) 要素を、 プログラムの中で書き表すための式/表現 (expression) ということなのですが、 これでは少しも説明になっていませんね。もう少し手前から語らせてください。
現代のコンピューターは信じられないくらい高速に計算をすることができますが、 どのような順番で何を計算するかの組み合わせを適切に指示してやらない限り、 何か意味のある計算をすることはできません。そのような指示のために人間が書くのが「プログラム」です。
プログラムには「静的な」意味と「動的な」意味の2つの意味がある、といわれることがあります。 Wikipedia にも static semantics と dynamic semantics という項目があります。
「静」の段階で行われるのは、いわば、翻訳です。 C++ を始めとするプログラミング言語で書かれたプログラムは、専門的な技術を要求するとはいえ、 一応、人間が読み書きできる文章のような形をしています。 この文章を直接そのままコンピュータが理解してくれるわけではありません。 コンピュータの理解できる形、 例えば電圧の高低で表現された 0 と 1 の列によるコンピュータへの指令に変換してやる必要があります。 この翻訳をしてくれるのも 「コンパイラ」や「インタプリタ」と呼ばれるまた別のプログラムなのがややこしいところですが、 とにかく、「人間向けのプログラミング言語の文章を解釈して、コンピュータ向けの指令に翻訳する」 というのが静的な側面です。
この「静」の段階では、プログラムはまだ、動いていません。 車の自動運転を制御するプログラムが書いてあっても、この段階ではまだ車は1mmも動きません。 車を動かすための指令が用意されるだけです。 きらびやかに音や映像を奏でるゲームのプログラムが書いてあっても、この段階では何も目に見える変化は起きません。 ただ静かに、プログラムがコンピュータに読み取れる形へと翻訳されるのみです。
解釈されたプログラムを実際に指令としてコンピュータに実際に与えてからが「動」の段階です。
ここで始めて、プログラムがそこに書かれた意味で動き出します。say("あけおめ!")
と書かれたプログラム(を翻訳したもの!)が動くと、
手元のパソコンから「あけおめ!」と合成音声が空気を振るわせて挨拶してくれます。
コンピュータのプログラムにとっての晴れの舞台は「動」の世界でしょう。 プログラムを受けとって使う人が触れるのは、つまり現実の世界に直接影響しているのは、 「動」の世界でプログラムが何をするかなのですから。 ところが、constexprは、すなわち、村上さんの興味が注ぎ込まれた対象は「静」の段階の住人でした。
コンパイルは祈りに似ている。世界よ斯くあれという祈りだ。
日経ソフトウエア5月号 「constexpr」が開くコンパイル時プログラミングの世界 - ボレロ村上
人間向けのプログラムからコンピュータ向けの指令への翻訳を専門用語で「コンパイル」と呼びます。 彼はそのコンパイルが祈りに似ているという。なるほど確かに、世界は未だ動かずとも、「斯くあれ」 という思いの種子はすべて練りこまれているということかもしれません。
さきほど「静の段階では、プログラムはまだ動いていません」と書きました。 話をひっくり返すようですが、ある意味で、この説明には穴があります。 というのも、翻訳というのは、それ自体がかなり高度な計算なのです。プログラミング言語ではなく、 英語や日本語の翻訳を思い浮かべていただいても構いません。 「この "it" は文章中のどこを指しているのだろうか?」を理解するには、 複雑な文法構造を正確に読み解く計算が必要です。 人間の脳はなぜかこういうものを瞬時に把握できてしまうようですが、 コンピュータにわかる計算手順として書き出してみると、これはとても難しい。 同じように、プログラミング言語の翻訳でも、 「ここに "変数 x" と書いてあるのはプログラムの中の何を指しているんだっけ?」 といった関係を完全に把握していないと、コンピュータへの正しい指令に変換することができません。 翻訳を行うプログラム(「コンパイラ」)は、こういった関係を計算で導き出す演算能力を隠し持っています。
しかし、いくら計算能力を隠し持っていたとしても、その能力は、 プログラムをコンピュータにわかる言葉へ翻訳する用途にしか使われないはず。 どんなプログラムを書いたって、「静」の段階で起きることは翻訳だけで、 「動」の段階にならなければ好き勝手な計算なんてできないはず。
そのはずでした、Erwin Unruh という C++ プログラマが、 翻訳しようとすると、コンパイラが素数(1以外の数で割り切れない数)を計算して並べ始めてしまう という魔法のようなプログラムを書くまでは。翻訳しかできないはずの「静」の世界の計算能力を、 言葉遊びのような不可思議なプログラムを翻訳させることで、 どんな計算でもできる力として切り出せてしまうことがわかったのです。
「テンプレートメタプログラミング」と呼ばれるこの発見そのものは、 「C++ という言語を発展させすぎると、こんなおかしなことが起きてしまうけれど大丈夫なのか?」 という問題提起として提出されたもののようです。C++ コミュニティの反応は「是」でした。 「おかしなこと」大歓迎。 詳細は専門的すぎるので省略しますが、 この不可思議な計算能力を避けるのではなく全力で活用することで、 「動」の世界のプログラムだけでは実現できない便利な/面白いプログラムが書けることが徐々にわかってきました。 テンプレートメタプログラミングは、21世紀初めの C++ プログラマ達の間での最先端の流行となりました。
当時のC++プログラマが結集したコミュニティの一つに Boost があります。 いくらか技巧に走りすぎていた傾向はありますが、 テンプレートメタプログラミングを初めとする様々な技をちりばめた高度な価値のあるプログラムを創りあげ、 「ライブラリ」として公開していました。 今この記事を書いている私も、こういったテンプレートメタプログラミングや Boost に熱狂したうちの一人で、 Boostライブラリの解説書 を出版するに至るなどしています。
そして以前インタビュー記事を拝見して驚いたのですが、 村上さんはその私の解説書を契機に、C++にハマっていったらしい。
当時、C言語がパワーアップしたようなC++というものがあるらしいというのを聞いて、 ちょっとやってみようかなとコンパイラをダウンロードしてみました。そのときにいくつか買った本の中にあったのが 「Boost C++ Library プログラミング」というC++のBoostライブラリの解説書です。
その本には様々なテクニックが紹介されており、 その中に「テンプレートメタプログラミング」という技法がありました。 これが面白くてどんどんのめり込んでいきました。
この本の執筆途中、Boostのプログラムが「どんな風に使えるか」だけではなく、 「どんな風に作られているか」に読者が触れられる本にしようと考えていたことを思い出します。 その甲斐がありました。
とはいえ、この時期は、テンプレートメタプログラミングが大いに盛り上がり、 解説書まで出てしまったような時期です。 どんな技法を編み出せば「静」の世界で実のあるプログラムが書けるのかを明らかにする、 一番面白いところの鉱脈は既に先達に掘られてしまった後とも言えます。 完全に私の想像でしかありませんが、村上さんはこの数年の差を惜しんでいたのかもしれません。 誰も前にいない、未開拓の技術の荒野に足を踏み出す機会を望んでいたのではないでしょうか。 そんな時に現れるのが、constexpr です。
C++ に限らずプログラミング言語というのは、誰か人間が隅々まで設計した人工言語です。 自然言語と違って、設計者が「この文法はもうやめよう」と作り替えたり、 「新しいこういう文法を増やそう」と作り直せば、新しい書き方が増えていきます。 C++ は、標準化委員会という委員会での議論で常に再設計が進められており、 最近では三年に一度ずつ、言語の仕様が改訂されています。
2011年の改訂でC++に追加された constexpr には、その背景の一つとして、 テンプレートメタプログラミングに対する反省がありました。 あまりにも「無理矢理」すぎないか?という反省です。 元々あったC++という言語の翻訳の過程をいわば「悪用」して書かれるテンプレートメタプログラミングは、 本来のプログラムに寄生した別の生き物のような、異様な見た目をしています。 一見するととてもプログラムには見えない代物です。 「翻訳(コンパイル)の途中で動く計算を書きたいというのが目的ならば、 テンプレートメタプログラミングという寄生言語ではなく、C++で自然に書けるようにすればよいのでは?」 「C++で書いたプログラムの一部に "constexpr" と注釈をつけたら、 その部分は「静」の世界で翻訳の途中に動かすという決まりにしよう。」 という流れで constexpr が生まれました。
言うほど簡単なことではありません。プログラムから機械語への翻訳のために作られた機構を動かすのと、 C++という言語を完全に動かすのには、大きな差があります。 プログラムを動かすには
静 ===> 動
の二つの段階があると上で書きました。「静」の段階でこのような完全なC++プログラムを動かすとなると、 「静」の段階を動かすために、そこに再帰的に「静 ===> 動」の段階が入ることになるでしょう。
(静 ===> 動) ===> 動
この一番左の「静」の中でもまた、翻訳途中で動く計算を動かすことがあるかもしれません。
((静 ===> 動) ===> 動) ===> 動
(((静 ===> 動) ===> 動) ===> 動) ===> 動
...
最初からそのように設計されたプログラミング言語であれば、このような無限退行に陥らない、 あるいは陥っても問題ないように作ることはできます。しかし、 C++ という既にある巨大な言語をいきなり入れ子にするのは非常に難しい。 2011年版のC++では、C++言語のうち、動かすのがとても簡単な、ごくごく限られた片言のようなプログラムだけを constexpr と印をつけて動かしてよい、という決まりになりました。
さて、ふたたび C++ プログラマ達の前に、そして今度はボレロ村上の前に、手つかずの荒野が現れました。 この「片言のようなプログラム」にはどれだけの表現力があるのか? そのたどたどしさに見合った簡単な小物しか作れない程度のものなのか? それとも、幼子のおしゃべりのような言葉だけで、シェイクスピアの戯曲のような傑作を綴ることはできるのか?
プログラマとしての村上さんの代表作の一つに、constexpr による コンパイル時Ray Tracing があります。"Ray Tracing" というのは、コンピュータで、 現実感のあるいわゆる「3次元の」映像を描くための古くからある技法です。 太陽や電球から飛んでくる光の線の反射、屈折、拡散、減衰などを含めた軌跡を何十万本も計算して混ぜあわせることで、 映像のこの場所はこの色になるはずである、という色を一点一点ずつ求めて一枚の画像にしていきます。 つまり昨今のCGを駆使した映画やゲームなどの「動」の世界のプログラムで使われる大規模な計算を、 「静」の世界でconstexprを使って作り出してしまったわけです。 Ray tracing に限らず、他にも多数の複雑なプログラムを constexpr だけで実現したものは、 Sprout と名付けられたライブラリ(プログラム集)として公開されました。
constexprだけで複雑なプログラムを書く、ということに挑戦したのは無論村上さんただ一人というわけではなく、 村上さん自身のブログでも、 例えば id:RiSK 氏の "CEL" ライブラリという先駆者の存在に何度も言及されています。 とはいえども、constexprだけでここまでのことを、ray tracing を実現してしまったり、 コンピュータに音声をしゃべらせる音声合成を実現してしまったりと言ったレベルの深みを追求してしまったのは、 彼をおいて他にないでしょう。 その名は国内にとどまらず、 たとえば米Microsoft社のC++コンパイラ開発チームは、 constexprを扱う機能をテストするのにSproutを使っているそうです。 ボレロ村上といえばconstexprというだけでなく、 世界レベルで、constexprといえばボレロ村上であるとすら言えます。
少しだけ補足としては、 「非常に簡単な機能だけを組み合わせてとんでもなく複雑なものを作る」のは、 プログラミングという作業そのものがある意味まさにそれなのであって、 constexpr でなんでも作れるという想像上の可能性だけであれば、そこまで驚くべきことではありませんでした。 条件による処理の分岐と、処理の繰り返しさえ記述できればどんな計算でも書けるという 構造化定理 なども知られています。しかし理論はどうあれ、具体的なモノとしてその理論を実現に移すのには、 さまざまな技術的な困難を解決する必要があります。 完全に技術的詳細の話になってしまいますが、 constexprに課せられた「再帰の深さ」というパラメタに関する制約のせいで、 他のプログラミング言語で培われた技法をそのまま転用するのはほぼ失敗します。 その困難を「倍分再帰」 と村上さんが呼んでいたテクニックなどで乗り越えていく様は胸躍るものがありました。
さらにもう少しの補足。constexpr というのは自然に生まれたものではなく、 C++標準化委員会の誰かが考え出して人工的に設計したものなのだから、 何ができるだとか何ができないだとかは設計の時点でわかっている話なのでは? という疑問を持たれた方もいらっしゃるかもしれません。しかし例えば、ピアノを最初に作った楽器製作家は、 そこから生まれる無限の音楽の広がりをすべて「わかっていた」と言えるでしょうか。 優れたピアニストの演奏が未知の世界を聴かせてくれるように、人に作られたC++/constexprという道具の上であっても、 そこに豊かな未知の世界は広がっています。
プログラミングというのは、第一義的には、ひどく工業的で実用的なもので、 どれだけ現実の世界の問題を解決するのに役に立つか、という軸によって評価されます。 この評価軸に沿うと、コンパイル時 Ray Tracing のような constexpr 超絶技巧は……言い切ってしまいますが、 あまり意味のないものです。 なるほど constexpr でちょっとした簡単な計算をするのは実用面でも有用な応用がいくつもあります。 しかしいくらなんでも、コンパイル時の計算で美麗な画像を生成する必要があるとは考えにくい。 「constexprをやたらと酷使」という斜に構えた表現になってしまうのも無理はありません。 普通の意味で求められる範囲を遙かに超えて、 何故こんなことをするのか実用一辺倒の人には理解できない constexpr の使い方をしていたのが村上さんです。
その一方で、世のプログラマ達は、 卑近な有用性などにとらわれない純粋な技術的可能性の追求に憧れる心も、持ち合わせています。 「やたらと酷使」という表現には大いに愛も込められていると感じます。 どうしても現実的な動機に引きづられてしまい、 純粋な技術的興味だけをトコトンまで突き詰めることができない我々との、 間の一線を飛び越えていってしまった村上さんへの賞賛。
それにしても、ことここに至って、なぜ自分はconstexprにこうまで熱心になっているのか、理由をあらためて考えても謎だ。 モチベーションは分からない。
日経ソフトウエア5月号 「constexpr」が開くコンパイル時プログラミングの世界 - ボレロ村上
ご本人ですらこう書いていらしたものを勝手に推察するのもおこがましいですが、 それでも敢えて考えるに、やはり村上さんは、constexprの原始的で素朴な記述をゼロから積み上げて、 現代的な高度なプログラムに至るまでの過程を、 自分の手でたどって体得することに熱中していたのではないかと思います。 縄文時代の土器を技法の面でも精神的な面でもたどった上で現代に陶芸をする、 というアーティストしての足跡と何か共通するところがあったのだろうか、 と思わず安易に考えてしまいますが、その考察をするには私自身にあまりに縄文・陶芸の素養が足りません。 まずは C++ マニアとしての視点だけを投げ出して、ここで筆を置こうと思います。