古典四重奏
教科書のプログラム 4 本を一気に言語へ投げた。各々が疑わしい soft spot を狙う: 行列積はネストしたコンテナへ、Game of Life は世代更新へ、数独は書いて戻すバックトラックへ、桁 vector の階乗は固定幅整数へ。4 本とも両 native backend で一発正解、新バグゼロ。probe 駆動の修正 26 リリースの後、何も転ばない一日は空振りの probe ではない —— それは方法全体が目指していた測定であり、他と同じように記録される。
この連載のこれまでの probe は全て「壊れていそうなもの」を狙い、大半が当たった。Part 最後の
この話は賭けを反転する: 古典プログラム 4 本を、各々が最近の仕事で疑わしくなったサブシステムに
寄りかかるという理由で選び、一気に走らせて何が転ぶかを見る。行列積はネストしたコンテナ
(Vec[Vec[float]])とその下の copy-on-store 意味論に寄りかかる。Game of Life は「現世代を
読みながら次世代を書く」に。数独は「書いて戻す」に —— 数字を書き、再帰し、undo が本当に undo
することを信じねばならないバックトラッキングソルバだ。そして大数の階乗は、整数の物語が明示的に
与えないもの —— 任意精度 —— に、わざと寄りかかる。
4 本、バグゼロ
4 本とも、interpreter と C backend で、初回実行で正解した。行列積は厳密。glider は 4 世代ごとに
斜めへ 1 セル移動し、8 世代後には Conway の言うとおりの場所に、5 セルで残骸なしに座る。9×9 の
パズルは既知の唯一解に解ける。そして階乗の段は固定幅の物語を二幕で正直に語る: native の 21!
は両 backend で同じ負数へ wrap し —— 同じ wrap が同一に、というのは 2 つ前の Part の幅統一への
小さな勝利でもある —— それから桁の vector から 30! の 33 桁が、最後のゼロまで正しく出てくる。
最もバグがありそうな 4 箇所で、新バグゼロ。26 リリースの発見と修正の後で、これは退屈な結果では
ない。方法がずっと指していた測定だ: 言語のテスト済み表面が、教科書の一章分を越えて育った。
四重奏は examples ディレクトリに加わる —— うち 1 本は新しい名前で。Game of Life は構文糖の
ショーケースとして既に存在していて、probe で歴史を上書きするのはそれ自体が小さなバグだった
だろうから。
唯一の痛みと、Part の帳簿
一つだけ痛んだが、それは意味論でなくメッセージだった。行列コードはセルを小さな多相 helper 越し
に読む。型検査器が s + get a i * get b j を見る瞬間、helper の要素型はまだ未解決の変数だ ——
だから数値演算子は int に default し、やがて出る「expected float, got int」は本当の原因から
遠い無実の式を指す。defaulting の規則そのものは残る(2 つ前の Part で秤にかけ文書化した)。
変わったのはエラーが自分を説明するようになったことだ —— hint が defaulting を名指し、2 つの
逃げ道を示す: パラメータに注釈するか、片方のオペランドに ascription を付けるか。かくして Part の
帳簿: channel は閉じ方を学び、supervisor は諦め方を学び、select は不要と判明し、capture 解析の
バグが pub/sub broker の中で死に、古典 4 本がクリーンに走った。「終わり」のツールキットは 3 本の
小さなプログラムが要求したから存在する。四重奏は土台の残りが持ちこたえていると言う。次の Part が
何を測るかは、いつものように、まだ決まっていない —— probe が選ぶ。