単相化こそが辞書
言語最大の未決設計課題 —— 型クラスを入れるか —— は一午後で消滅した。答えが出たのではなく、codebase の中で既に半分答えられていた、より小さな問いに縮退したのだ: 等価は最初からずっと、別の理由で建てられた機構に運ばれて、静かに多相だった。順序は 3 行の削除でそこに合流した。generic sort は無料で届き、pairing heap の comparator 税は消えた。
この 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 は ずっと具体的な場所に向けられた: プロジェクト自身のサイトのライブゲームだ。それは、実はずっと 壊れていたのだった。