日本語を打ってみる

シリーズ最安の probe: 言語に日本語を打ち込み、各操作が何をするか報告する。byte 指向の str は正直で文書化された決定だった —— 5 文字の挨拶に長さ 15 は正しい、ただテキストプログラムが欲しい答えではない。probe は全ての「間違った」答えを二つの箱に仕分けた: byte で良いから良いもの、codepoint view が要るもの。修正は意図して小さく —— UTF-8 境界を尊重する 2 つの builtin、その上の 3 つの prelude 合成 —— そして偶然に歴史的だった: 同一リリースで 4 backend 全てに生まれた初の機能。

mereunicodestringsstdliblanguage-design

この probe はシリーズのどれよりも安かった: 5 文字の日本語の挨拶を取り、文字列ライブラリを 一巡させる十数行。サーバも、ゲームも、ベンチマークも無し —— ただ言語に日本語を打ち込み、 返ってきたものを書き留める。結果は「記録する価値がある」という種類の予測どおりだった。 str_len は 15 と言った。index は \xe3 と言った。反転はどの言語のテキストでもないゴミを 生んだ。substring は文字を真っ二つに切った。どれもバグではない: memory-model の docs は str は byte だと明記していて、全操作は約束どおりのことを厳密にやった。だが「文書化済み」と 「テキストに使える」は別の性質で、作者が日本語で書く言語は、このリリースまで こんにちは の 文字数を数える方法を持たなかった。

二つの箱

probe の有用な一手は、「間違い」を一山として扱うのを拒んだことだ。驚きの答えはそれぞれ二つの 箱のどちらかに入った。箱一: byte が正しい答え。 連結、等値、ハッシュ、ASCII 区切りでの split、バッファサイズ用の byte 長 —— UTF-8 はこれらが生 byte で動くよう設計されていて、probe は 各々を日本語文字列に対して検証した。修正不要。正直な注記は「これらは最初から壊れていなかった」。 箱二: プログラムの意味は文字。 「何文字か」としての長さ、「n 文字目」としての index、 substring、反転。これらは codepoint view が要り、user code で偽装するのは bitwise 演算子の 無い言語で UTF-8 の lead-byte デコードを再実装することを意味する —— probe は試み、div と mod で 建てた 40 行のデコーダを生み、その醜さを forcing measurement として綴じた。

最小の正直な修正

設計の問いはスコープだった: 完全な Unicode 層 —— grapheme cluster、正規化、locale 対応 collation —— か、codepoint view か?答えは箱から来た: 箱二の全ては codepoint で解け、probe の 何も grapheme を要さなかった。だからリリースはちょうど 2 つの builtin を足した: utf8_len、 そして文字列を単一 codepoint 文字列のリストへ分解する utf8_chars。残り全部 —— n 文字目、 codepoint 安全な substring、反転 —— は Mere 自身で書かれた、上に合成する 3 つの短い prelude 関数だ。str は byte のまま。codepoint view は綴りで opt-in する。偶然のマイルストーン: prelude はどこでも走らねばならないので、両 builtin は 4 backend 全て —— interpreter、C、wasm、 LLVM —— に同一リリースで着地する必要があった。プロジェクト史上初の、全所同時に生まれた機能だ。 それを強制した規則(「prelude は universal な builtin のみ使ってよい」)は、供給できない prelude で LLVM backend がクラッシュするという痛い道で発見された。こんにちは の長さはいま 5、 反転は はちにんこ、そして残る gap —— grapheme、は + 結合記号がまだ 2 codepoint である世界 —— は docs の、プログラムが必要とするのを待つ他の gap たちの隣に書き留められている。

← Back to Mere: 言語を作る