単相化こそが辞書

言語最大の未決設計課題 —— 型クラスを入れるか —— は一午後で消滅した。答えが出たのではなく、codebase の中で既に半分答えられていた、より小さな問いに縮退したのだ: 等価は最初からずっと、別の理由で建てられた機構に運ばれて、静かに多相だった。順序は 3 行の削除でそこに合流した。generic sort は無料で届き、pairing heap の comparator 税は消えた。

merepolymorphismtype-classesdesignlanguage-design

この Part は、方法の経済が変わった話だ。これまでの Part は実物のツール —— サーバ、ゲーム、 inspector —— を建てて言語に成長を強制し、ツール 1 本に数日を費やした。だがメモリモデルが完成し soundness の穴が塞がった今、残る問いは十分に精密になっていて、10 分の probe で決められる: 問いに触れる最小のプログラムを書き、測り、数字に次の仕事を選ばせる。この Part はそういう probe 6 本と、それが生んだ 9 リリースの記録だ。最初の probe は言語最大の未決設計課題に向けられ、 見つけたものは課題に答える代わりに、課題を溶かした。

問いと、問いを組み替えた測定

台帳上の問いはこうだった: この言語に型クラスは要るか?前 Part の generic pairing heap が正直な 測定値を出していた —— generic コンテナは書ける、だが全操作に comparator closure を通すのは目に 見える税で、そして <型変数越しに使うことは端的に不可能(fn a -> fn b -> a < b は引数を int に固定する)。このセッション用に起草した設計ノートは中間案 —— 具体的な呼び出し地点での dictionary 合成 —— を提案していた。そしてセッションは、ここのセッションが始まるべき仕方で 始まった —— コードが実際に何をしているかを読むことから —— そして提案より良いものを見つけた: 等価は既に型変数を通っている。ずっと前からだ。fn a -> fn b -> a == b は正真正銘の多相で、 そのために型クラスを建てた者は誰もいない。だから本当の問いは「どう制約を足すか」ではなく、 「なぜ等価は制約なしで許されているのか?」だった。

なぜ等価は許されているのか

別々の理由で建てられた二つの機構が、合わさって辞書の役を演じていたと判明する。interpreter では、 実行時の値は常に具体なので、構造比較の関数はただそれを歩けばいい —— 型情報は不要だ。コンパイル される backend では、多相関数は単相化される: 具体型での使用ごとに特殊化された instance が 作られ、その instance の body の中では == の operand は具体型を持つ —— まさに derive 機構が 型ごとの等価関数を emit して扱う、あの状況だ。scheme が制約を運ばないのは、コード生成の時点で 制約すべきものが残っていないからだ。Haskell は実行時に辞書を渡す。この言語では単相化器こそが 辞書であり、すべてコンパイル時に解決される。前 Part の修正群 —— pristine clone、多重 instance への遅延昇格 —— が、この機構をいつの間にか信頼に足るものにしていた。順序を塞いでいたのはただ 一つ: 「未解決の比較対象は int に default する」という型検査器の歴史的規則だった。

default を消す

修正はその規則の削除だった —— 順序は operand を unify して、そこで止まる。等価がずっとして きたことと寸分違わない。int default に頼っていたプログラムは instance 化が面倒を見るので今も 型が付く。suite の失敗は、偶発的なスロット番号を固定していたアサーション 2 件だけだった。 そして帰結は無料で連鎖した。prelude が既に正直なスタイルで書かれていたからだ: list_sort は comparator fn a -> fn b -> a < b を渡す list_sort_by として定義されている —— 黙って int 専用 だったその定義は、default が死んだ瞬間に generic になった。タプルのリストのソートに、もう注釈も comparator も要らない。pairing heap の手書き・型注釈付き comparator は、多相の 1 行に潰れた。 コンパイル結果は機構を隠さず見せる: instance は derive された tuple 比較関数 —— 3 Part 前に derive-ord が建てた、まさにあれ —— を呼んでいる。

決めないことで決まったこと

これが何であり何でないかは、精密に言っておく価値がある。型クラスは依然として無い: 型の順序を 構造と別のものとして宣言する術は無く、scheme に制約は現れず、instance 宣言も無い。在るのは derive 一家 —— show、JSON、等価、順序 —— が任意の具体型で、そして今や任意の型変数越しに使え、 単相化が解決機構を務める、という状態だ。これはこのプロジェクトで同じ設計本能が現れた三度目だ: uncurrying はちょうど飽和した呼び出し地点だけを最適化した。derive 一家はちょうど具体型でだけ 特殊化する。そして多相順序は、instantiation が物事を具体にする、ちょうどその場所で解決する。 一般問題が容易になる特定の一点に賭け、プログラムがそれ無しで生きられないと実証するまで一般機構を 建てない。型クラスの問いは大きくは開いたままだ —— ユーザ定義 instance は forcing program を待つ 将来課題として残る —— が、それが運んでいた書き味の積み荷は、それ無しで届いた。次の probe は ずっと具体的な場所に向けられた: プロジェクト自身のサイトのライブゲームだ。それは、実はずっと 壊れていたのだった。

← Back to Mere: 言語を作る