OCamlは、母だった?
初めて OCaml に触りました。Julie という、おもちゃの Julia 実装を書くためです。
触る前は、GC つきで WebAssembly にコンパイルできる、ちょっと変わった関数型言語だと思っていました。触ったあとに残ったのは、そういう機能の話ではありませんでした。OCaml は、新しい言語を生み出す力に、何か特別なものを持っているのかもしれない。そう思うようになりました。
ひとりの思いつきでは、なさそうだった
自分の感想だけでは、心もとない話です。だから、他の人たちが同じことをしていないか、調べてみました。
Rust の最初のコンパイラは、Rust では書かれていませんでした。OCaml でした。Graydon Hoare が Rust を作り始めたとき、自分自身をコンパイルできるようになるまでの何年か、Rust という新しい言語は OCaml の上で育っていました。
WebAssembly の話も、似ています。WebAssembly という仕様そのものの「公式のリファレンス実装」——ある WASM バイナリが本当に仕様どおりかを判定する、いちばん権威のある判定者——は、いまも OCaml で書かれています。ブラウザの実装が正しいかどうかを、最終的に照らし合わせる相手です。
新しい言語を生み出す現場に、OCaml がくり返し居る。これは、ひとりの思いつきではなさそうでした。
同じものを、二つの言語で書いてみた
とはいえ、印象だけでは弱いので、実際に比べてみることにしました。同じ小さな言語処理系——型階層、多重ディスパッチ、struct、ゲームループのデモ——を、OCaml と JavaScript の両方で、同じマイルストーンまで書いてみたのです。
違いがいちばんはっきり出たのは、値を表示する show() という、地味な関数でした。
JavaScript 版では、switch 文で値の種類ごとに分岐を書きます。数値、文字列、真偽値、構造体、それから、あとから足した Range という値の種類。この Range の分岐を、書き忘れました。
何が起きたか。コンパイルは通ります。警告も出ません。プログラムはしばらく正常に動き、まったく関係なさそうな場所で、こう言って落ちました。
TypeError: Cannot convert undefined or null to objectRange を表示しようとした場所ではなく、その値を受け取った先の、もっと奥のどこかで。JavaScript の switch は、「値の種類はこれで全部か」を知りません。知らないので、抜けがあっても黙っています。抜けの報いは、原因から遠く離れた場所で、あとになってから届きました。
OCaml 版で同じ抜けをやると、コンパイルが通りません。
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched: VRange (_, _, _)行番号つきで、足りない分岐の形まで示して、教えてくれます。実行する前に。
なぜ、これが「母」に効くのか
新しい言語を作るとき、値の種類はどんどん増えていきます。整数、浮動小数点、文字列、真偽値——ここまでは最初から居る。構造体、範囲、クロージャ、複素数——あとから、機能を足すたびに増えていく。
種類が増えるたびに、「これを扱っている場所、全部に手を入れたか?」という問いが、何度も何度も戻ってきます。実際、この Julie という処理系を育てている間、この問いに何十回も当たりました。値の種類を一つ足すたびに、表示・比較・型タグづけ・演算子・struct のフィールド代入——扱っている場所ぜんぶを、思い出さなければいけない。
JavaScript でこれをやると、思い出す責任は、書き手ひとりの記憶力に丸ごとのっています。OCaml でこれをやると、コンパイラが名指しで、抜けを教えてくれます。「新しい言語を生み出す」という作業は、まさにこの「値の種類が増え続ける」という営みそのものです。だから、この性質がいちばん効くのだと思います。
Rust の最初の何年か、WebAssembly のいまの判定者。新しい言語がまだ若くて、値の種類が毎週のように増えている時期を、OCaml がくり返し支えてきたのだとしたら——それは偶然の一致というより、この一つの性質が、ちゃんと選ばれ続けてきた理由なのかもしれません。
母、と言い切るには
もちろん、これだけで「母」と言い切るのは、乱暴だとも思います。OCaml にも、癖はたくさんありました。エラーメッセージは JavaScript よりずっと不親切なことがあるし、let rec ... and ... の相互再帰グループを正しく組むのに、この処理系を育てている間、何度も同じ種類のコンパイルエラーで足を止められました。母は、優しいばかりではありません。
それでも。値の種類を一つ足すたびに、「抜けはないか」と、コンパイラのほうから聞き返してくれる。この一つの性質だけは、新しい言語がまだ揺りかごの中にいる間、ずっと寄り添ってくれるものだと、実感しました。
Rust も、WebAssembly の判定者も、育つ場所として、この寄り添いを選んだのかもしれません。わたしの Julie も、気づけば同じ場所で育っていました。
つづきは、また、そのときに。
コメント
この記事は fediverse に流れています。あなたのアカウントから返信すると、ここに並びます。
まだコメントはありません。