Azuki 1.6のアルファ版でUnicodeの結合文字をサポートしました。ここで起こった失敗談を一つ記録しておきます。
Unicodeの結合文字についてUnicodeの仕様書を参照すると、どんな文字に対しても結合できるものであり、Unicodeの仕様としてはいかなり文字の並びであっても不正とは扱わないと書かれています。しかし、Azukiでまさにこのような考えで実装を行ったところ、思わぬ例外ケースが出てきてしまいました。「改行文字に結合文字が結合してしまう」ケースです。
まず、Azukiはプロジェクト発足当時から改行コードをCR、LF、CR+LFの3種類しかないという前提を置いてあらゆる機能設計を行っていました。そして、結合文字サポートに当たって、結合文字の前にどんな文字があるかを気にする必要は無いと考えてしまいました。その結果、改行コードの直後に結合文字がきてしまった場合にもAzukiはこれらを一つの書記素クラスタ(grapheme cluster)として扱うこととなり、「新しい改行コード」を生み出してしまいました。大前提であった「改行コードは3種類」を、うっかり崩していたわけです。そして大前提を崩した結果、ある処理中に特定条件が整うと無限ループに陥るバグが発生しました。
落ち穂を拾います。結合文字の前にどんな文字がきても良いのはUnicodeの話であって、Azukiの話ではありませんでした。Azukiにとって改行コードは文字であるだけでなく、行の区切りとして使われる特別な記号であり、そこに大きな前提を置いていたことを意識する必要があったと反省しています。
どうも最近出くわすプログラム不具合の多くは前提の見落としが原因になっています。assertionを多用することで見落としを防止できる前提もありますが、今回のケースではプログラム的な対策も難しかったと思います。結局のところ作っている人間の思考レベルで作り込むバグが一番対処しにくい。仕様書に記しても対策になりません。設計時に慎重になれば見つけられる可能性は上がりますが、そのかわりに生産性が落ちます。日常業務が終わった後の短い時間を少しずつ使って作っているAzukiの現状では、慎重になりすぎると何も形にならなくなってしまいますしね。難しいものです。
参照:
- The Unicode Standard, Version 5.2 – 2.11 Combining Characters