boolean問題
いくつかの言語でプログラムを書いていると、注意しなければならないことがある。そのうちの1つがboolean問題だろう。
boolean問題というのは、データ型の値のうち何が真で何が偽かが言語によって異なるという問題である。
具体的に言えば、整数型の0、文字列型の空文字列""、ヌル(null, nil)が真であるか偽であるかが言語によって異なるということである。また、データ型としてのbooleanが定義されていない言語もある。Windows APIでは、ブール型はBOOLと定義されているが、実質は整数型である。また、マクロでTRUEは1、FALSEは0と定義されている。
boolean問題は、if文のような条件分岐で問題になる。
if (x) { ... }
と書いたときに、変数xにどのような値が格納されているかで真偽が分かれるが、それが言語によって異なるとソースを追っていくことが難しくなる。プログラムの移植時にも思わぬバグが発生するかもしれない。C言語では、xが整数で0以外であれば真、0のときのみ偽に評価される。Windows APIでは上記のように純粋なboolean型が無いが、APIが成功したかしないかを表す擬似BOOL値を返すものが多い。msdnの説明には、「成功時には0以外の値を返します」など一見わかりにくい説明が載っているが、その背景にこのようなことがあるからである。
一方Javaでは、boolean型を定義し、条件分岐ではboolean型のみを許し、それ以外はコンパイルエラーにするという方法でこの問題を乗り越えている。利便性のために幅広く物事を受け入れるのではなく、あえて受け入れを制限することにより曖昧さをなくそうという方法である。
Cではbool型がないので、x != yが0か1を返すことになる。それを使ってa * (x != y) などという演算ができた。これがJavaでは出来なくなっている。しょうがないので、boolean型のtrueを1に、falseを0に変換するtoIntメソッドを定義した。なお、似たような関数が実はMathematicaにもあり、Boole関数として組み込み実装されている。
前述のように、Cではint値を返す関数に対して、if (f()) ... と書くこともできる。コードとしてはコンパイルでき、意図したように動くが、コードの表現としてはどちらかと言うと変に感じるかもしれない。それは文脈が失われていることが原因である。
なので、整数値を返す場合については、if (f() != 0) ... と書くことをルールとして定めることにした。そのほうが可読性も上がりコードの意味がつかみやすくなる。一方で、Windows APIのように整数型を擬似BOOL型として使っている場合には、 if (f()) ... と書くことにする。if (f() != 0) ... と書いても実行上は問題にならないが、文脈としては違った解釈になってしまうためこうは書かないことにする。
論理的に整合していることとリーダビリティの確保というのは、ある意味で柔軟性と相反する。型の強い制限を導入するということが、1つの解決策かもしれない。よくあるインタプリタのスクリプト言語では従来の形式張った記法から解放するため柔軟性を強く意識していった結果、逆に意図しないことが起こるようになったと感じる。結局逆効果であったということである。そうした流れを経て、今は強い型付けがトレンドになってきているように思われる。あくまでも個人の感覚である。ほかの人はまた別なように思っているかもしれない。
実は昔は形式張ったことはやめようぜということで、柔軟なスクリプト言語を支持してきた。最近になって、自分の中で考え方が変わってきた。厳密性は重要だと気づき、意識するようになった。結局のところ、「ベストなプログラミング言語」はいつまでたっても決められないものなのかもしれない。
追記:Haskellは型に非常に厳密であるが、次のコードが動かないのが気に障る。
average list = (sum list) / (length list)
解決策としては、fromIntegralを使う。ここまで厳密な必要があるのか?とも思ったりしている。まあ、世の中には色々な言語があるものである。