好きなあの子の嫌なとこ

This entry was posted by on Friday, 14 November, 2008

http://www.kt.rim.or.jp/%7ekbk/zakkicho/08/zakkicho0811b.html#D20081112-4

こういう聞き方はいいね。結局、よくわかっていないと本当の弱点はわからないものだし(ささださんに話を効くとRubyのまずいところをいくらでも語ってくれる説)。まあ中には本当にわかってんのかなあという回答もありますが、それはさておき。個人的には特にプログラミング言語そのものに対するこだわりが薄くなっているので自分で書こうという気はないですが、Haskellについてわかりづらいところがあると思うので説明しましょう。

>

The big implementations handle I/O in ways that don’t seem quite compatible with the standard. (In particular, outputting characters only outputs the low 8 bits — and then code gets built that uses this assumption to do binary I/O. Ick.)

読んだ瞬間「あ、あれね」という感じですけれども、まともに使ったことがある人じゃないとわかりづらいですね。

まず、Haskellにおける文字型(Char)は1文字=Unicode文字と定義されています。ところが実装がまずいせいでCharをputCharとかで出力しようとすると、最下位の1バイトしか出力されないんですね。「あ」が "B" になっちゃうという具合。そういう事情もあり、実際にはCharをバイト列であるかのように扱う流儀があります。その方が簡単なんですね。たとえばhGetContentsでファイルを読み込むと文字列が手に入るわけですが、この場合は普通に1バイトずつCharに収まっているのです。さらにバイト列として高速に文字列処理をするByteStringなんてのもありますし…

Text.Xhtmlというxhtml出力ライブラリがありますが(たぶんこれなんかはone of the big implementationsでしょう)、これは1文字がisprintじゃないときはChar単位で数値実体参照に変換するという実装になっていたんですが、そうすると生のUTF-8バイト列の日本語を入れたときに大変なことになるので、U+FFまでの文字はバイト列とみなして実体参照に展開しない、という極めてuglyなhackを入れた犯人は俺です。超すまん。今どうなってるか知らないけど。

さて、これで終わりなら話はまだ楽なんですが、続きます。Haskellの言語仕様上1文字=1Unicode文字という定義になっていました。これは字句的にもそのような定義になっており、Haskell98ではファイルのエンコーディングに関する議論は全く無く、ファイルはあたかもUnicode文字の連なりであるかのような仕様です。とはいえ実際そんなわけはない。ただ昔はこの仕様は別に守られておらず、たとえばGHCではファイルはLatin-1でエンコードされているという前提になっていて、Latin-1ならバイト列をそのまま通し、そうでないならコンパイルする、となっていました。が、もう何年か前の話ですが、GHCはファイルのエンコーディングをUTF-8とみなすという方針に変更し、ファイルの中身をUnicode文字の連なりとみなすということになりました。このため、これまでLatin-1でファイルを書いていた人が「俺の名前が入ってるとコンパイルできないんだけど!」とか微笑ましい相談をMLに投げたりしたものです。

サラッと書きましたが、実はこいつは合わせ技で非常に面倒なことになります。たとえばプログラムのコードで putStrLn "あ" って書くと、この"あ"はUTF-8でエンコードされているんですが実際には"\u3042"と同じ扱いになり、putStrLnは下位1バイトしか出力しないので"B"になる、とか楽しい展開が待っています。

で、ブログを作るとして、お気楽に「コメント」とかをプログラム中にリテラルの文字列か何かでそのまんま埋め込んでおいて、もちろんコンテンツはどこかにUTF-8のバイト列にして保存していたときにどうなるかは、みなさんにも想像がつきますよね? この辺、もうしばらくさわってないので最新の状況は変わっているかもしれませんが、Haskell界隈でi18nに燃えている人って心当たりがいないので多分放置されているんじゃないかな。

あ、それで、つまりstandard的にはChar=UnicodeなんだけどChar=Byteであるかのようなnon-standard wayな実装は結構いっぱいあってわけわからん、と言うのが元の話だというわけ。いやしかしあれだね、悪い方が良い原則を思い出すよね。

One Response to “好きなあの子の嫌なとこ”

  1. shiro

    うーむ、2000年前後までのScheme界を思い出します。標準仕様にはエンコーディングの話は入っていなかったけれど、巷の実装はほとんどLatin-1前提で、自分が実用コードを書くときにそれでは話にならなかったのです (自分の書く分については妙なworkaroundを噛ませて何とかするとしても、サードパーティライブラリの中でマルチバイト文字列をぶっ壊されるとやってられない)。小手先のworkaroundではどうしようもない、という認識がGaucheをつくるきっかけのひとつにもなったのですが…