Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

@A_kirisaki

HaskellでのQuickCheckとData.ByteString.Char8の罠

URL変換を伴う部分でQuickCheckで以下のようなコードをテストしていたときです。

-- SBS = Data.ByteString.Char8
spec :: Spec
spec = do
  prop " urlEncode/urlDecode" $
    \t -> t == (SBS.unpack . urlDecode . urlEncode . SBS.pack) t

こんな失敗が出ました。

test/Network/Api/UrlSpec.hs:20:3: 
  18) Network.Api.Url  urlEncode/urlDecode
       Falsifiable (after 3 tests and 1 shrink):
         "\883916"

\883916という値で失敗しているようです。ちょっとghciでチェックしてみましょう。

Prelude Network.HTTP.Types.URI> urlDecode $ urlEncode "\883916"
"\204"

もとの数から変化している……?これはなんとなくピンときました。内部でUTF-32を使っているのでエスケープしたとき\x10ffffまで取ることができるのです。
UTF-32 - Wikipedia
で、ByteStringは内部的には[Word8]ですからこれをこの文字(\883916)をWord8に突っ込もうとします。もちろん255までしか突っ込めません。するとどうなるのかと言うと、なんとHaskell側で勝手に下位8ビット以外捨ててしまい無理やり0x00~0xffの範囲に収めてしまうのです。

それはともかくなぜこんなことが起こったのか……。ByteStringChar8バージョンなのがミソでしたQuickCheckはテストデータとして任意のStringは作ってくれますが、任意のByteStringは作ってくれません。[Word8]なら任意のデータを作ってくれるのでそれをpackしてByteStringを作っていました。

そしてどこかのタイミングで「おっ、このData.ByteString.Char8って便利じゃーん」とか思いそれに変えました。Data.ByteString.Char8packString -> ByteString、つまりQuickCheckでテストデータを生成しようとするとUTF-32[Char]が生成され、それが[Word8]にまとめられる……というわけでした。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
1
Help us understand the problem. What are the problem?