一夜明けて…

ブログのネタ的には2日前なのだが、書いたのは昨日の話。PHP三項演算子(a?b:c)が左結合なのは、PHP言語仕様のバグだ、と書いた。一夜明けたらPVがとんでもないことになっていた。TwitterとかFacebookとか、あと、もちろん本家「はてな」とか、とにかくそこらじゅうでネタにされている(汗)。それだけ、PHPって流行っている言語で、この問題も注目されているってことなのね。まあ、言語としてPerlよりはマシだと思うね。ってか私Perl嫌いかも?しかしPHPにも型がないところが嫌いなんだけど。JavaScriptの型付きバージョンTypeScriptなんてものあることだし、この際PHPにもHindley-Milnerスタイルの(ML風の)型推論でも入れれば良いのに。この問題も言語に型システムが備わってないからコンパイル時にエラーにならないという遠因もあったんだけどね。
話を戻して、PHP三項演算子ね。左結合か右結合かの違いは、三項演算子自体がネストした場合に問題となる。例えば、引き算のネストa-b-cを考える。これにカッコをつけると、(a-b)-cとなるのであって、a-(b-c)ではない。2つのマイナス記号が連続したら、左側にカッコがついているとみなす。これが左結合。引き算が右結合だったら、a-(b-c)の方の解釈が正しい、ということになる。三項演算子の場合、a?b:cなので、ネストの可能性は3通りある。つまり、以下のようにaの部分、bの部分、cの部分がそれぞれ更に三項演算になっているという3通りの可能性。

  1. a1?a2:a3?b:c
  2. a?b1?b2:b3:c
  3. a?b:c1?c2:c3

ここで、2の場合、問題は起こらない。構文解析上の制約で、解釈は1通りしかない。つまりa?(b1?b2:b3):c。問題は1と3なんだけど、1については、(a1?a2:a3)?b:cとa1?a2:(a3?b:c)が可能。左結合なら前者で右結合なら後者ね。同様に3についても、(a?b:c1)?c2:c3とa?b:(c1?c2:c3)が可能。C系の構文を採用する言語の中でPHPだけが左結合などという非常識な仕様を採用している。そもそもCを真似して作ったのに、ここだけ「間違えて」実装しちゃったとしか思えない。で、それを後から「仕様だ!」って言い張っているんだろうなぁ。大人げないねぇ。素直にバグだって認めれば良いのに。実装のバグじゃなくて仕様のバグね。言語仕様として「三項演算は左結合です。」って言ってしまえば、その実装は正しい。私が言っているのは、仕様のバグ。つまり、仕様です、そう決まってます、と言っていること自体が間違っている。どう考えたっておかしいでしょ?
動作を考えてみればすぐ分かる。先ほどの1の場合、左結合でプログラムを動かすと「条件a1が真なら条件a2を、そうでなければ条件a3を条件として、それが真ならbを、そうでなければcを返す。」となる。右結合であれば、「条件a1が真であればa2を返し、そうでなければ、条件a3を改めて評価し、これが真であればbを、そうでなければcを返す。」となる。明らかに後者のほうが自然だよね。3についても同様で左結合だと、変な解釈しかできない。どちらも、型チェックのあるプログラム言語なら、処理系がエラーにしてくれるはず。長いことこの業界にいるけど前者(左結合)のようなプログラムって書きたいと思ったことも、そのようなプログラムに出会ったことも、ただの1度もないね。
あっちこっちで、ネタにされていた中に「今さら言語仕様変えるなんて、後方互換性はどうなる?」なんてのがあったけど、そんな非常識なプログラミングは存在しないだろうから問題ないと思う。その前に、後方互換性って今や死語じゃない?Appleにしたって、Microsoftにしたって、どんどん言語バージョンアップして新しいOS出すけど、昔のプログラム動かないよね?もっとも、文法的な問題で動かないというような話よりも、ライブラリがなくなったとか挙動が変わったとかいう理由が殆どだと思うけど。いずれにしても、昔のプログラムを救うことを考えるより、今後も続々と増えるであろう「C系の言語から入ってくる人」を救うことを考えるべきだね。