星にゃーんのブログ

ほとんど無害。

地味なバグと誰かの囁き声

最近(ここ数年?)malgoというプログラミング言語を作ってる。

今日、地味なバグを見つけた。厳密にはバグじゃない気もするんだけど、ユーザーの直感に反するような挙動をする。

例えば、Eitherを定義するとする。

data Either a b = Left a | Right b;

すると、これを読んだコンパイラは、内部的に以下のような定義を型の辞書に追加する。

data Either b a = Left b | Right a;

aとbが逆転してる。いかにも単純で地味なバグだ。

なぜこんなことが起こるのかというと、(ここからややこしい解説が始まるので適当に読み飛ばせばよい)malgoコンパイラが内部的に管理している型辞書の型変数名は、元のソースコードとは独立に決まるから。コンパイラは単に名付けるべき型変数の集合を求めて、適当な方法でリストに変換して、そのリストの先頭から順にa,b,c,...と名付けていく。「適当な方法」が集合{t1, t2}をリスト[t2,t1]にしたために、直感的にはaと名付けられるべきだったt1がbになり、bになるはずのt2がaになる。

とりあえず、「適当な方法」のところでソートをかけることで見た目だけは直した。 だが、これはまったく解決にはなっていない。本当に実装すべきなのは、「名付けるべき型変数の集合Sと、Sのそれぞれの要素tとその元となったソースコード上の型変数aの連想配列Eが与えられた時、Sの要素すべてにEに基づく名前をつけたもののリストLを返すアルゴリズムF:(S,E)->L」。 (ややこしい解説ここまで

つまり、これは簡単に解決する地味なバグではなく、コンパイルエラーのUXに関わるかなり重大なバグだった。単に集合をリストにしたものをソートするだけではダメだった。

ということについさっき、23時ごろ気づいた。ソートでとりあえず直した風なコミットをしたのが15時。そういえば、List.sortと書いたとき、脳裏に「ソートする意味は?本当にソートしたいの?」と囁く声が聞こえた気がする。

コードを書いていると、こういう囁き声が聞こえることがある。多分囁いているのは僕のプログラマとしての直感そのものなんだろう。今までの経験から、長期的(1時間ぐらい)にはこの囁きに従った方が良く、短期的(10分ぐらい)にはこの囁きを無視した方がいいことが分かってる。

短期的目標が片付く前に囁きに惑わされると、差分がどんどんぐちゃぐちゃになっていって、コード自体もぐちゃぐちゃになる。後片付けのリファクタリングはかなり大変だ。

一方で、長期的には、今回のように鋭い指摘であることが多い。しかし、1時間も経てば、たった一度の囁きなんてすぐに忘れてしまう。これはまずい。都度コメントにメモを取るのが良さそうだ。 -- TODO: ソートする意味は?ちゃんと考える みたいに。でもこういうTODOって大抵放置されて、バグが発覚した時に、その爆心地で見つかるんだよな〜。